From b601fa52ba5ad275fca183cd975c1a88a19e2de0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 13 Mar 2019 13:02:55 -0700 Subject: [PATCH 001/605] Bumped version to 0.89.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 85097dcb6526e9..c4033b81dfd680 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 89 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0b0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 07022c46f2d118428a06cc15aa86782d24d6d5c9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 13 Mar 2019 13:03:58 -0700 Subject: [PATCH 002/605] Version bump to 0.90.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index c4033b81dfd680..7b74c25d08ac74 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 -MINOR_VERSION = 89 +MINOR_VERSION = 90 PATCH_VERSION = '0b0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) From 42c9472a746169e9ee9b2544e20914fee83bbe5b Mon Sep 17 00:00:00 2001 From: Phil Hawthorne Date: Thu, 14 Mar 2019 13:08:23 +1100 Subject: [PATCH 003/605] Remove UTF8 decoding for Waze (#22020) Removes the UFT8 decoding for the Waze sensor, which broke in 0.89 Fixes #21739 --- homeassistant/components/sensor/waze_travel_time.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/sensor/waze_travel_time.py b/homeassistant/components/sensor/waze_travel_time.py index 83b4f3ad9340fa..96a4c747293e9c 100644 --- a/homeassistant/components/sensor/waze_travel_time.py +++ b/homeassistant/components/sensor/waze_travel_time.py @@ -218,7 +218,6 @@ def update(self): route = sorted(routes, key=(lambda key: routes[key][0]))[0] duration, distance = routes[route] - route = bytes(route, 'ISO-8859-1').decode('UTF-8') self._state = { 'duration': duration, 'distance': distance, From c78e332df36706e82edb135e2bc034090da882c6 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 14 Mar 2019 18:18:25 +0100 Subject: [PATCH 004/605] Bring back the boiler status (#22021) --- homeassistant/components/netatmo/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index 409358c2f04692..2d8b06dd466d0d 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -187,7 +187,7 @@ def device_state_attributes(self): "module_id": self._data.room_status[self._room_id]['module_id'] } if module_type == NA_THERM: - state_attributes["boiler_status"] = self.current_operation + state_attributes["boiler_status"] = self._data.boilerstatus elif module_type == NA_VALVE: state_attributes["heating_power_request"] = \ self._data.room_status[self._room_id]['heating_power_request'] From 90c878a7eda68df641334cdd5cdc97af6016c933 Mon Sep 17 00:00:00 2001 From: emontnemery Date: Thu, 14 Mar 2019 18:58:32 +0100 Subject: [PATCH 005/605] Update additional platforms to use new MQTT message callback (#22030) * Move additional platforms to new MQTT callback * Fix automation.mqtt --- .../components/alarm_control_panel/manual_mqtt.py | 12 ++++++------ homeassistant/components/automation/mqtt.py | 12 ++++++------ homeassistant/components/device_tracker/mqtt_json.py | 8 ++++---- .../components/mqtt_eventstream/__init__.py | 4 ++-- homeassistant/components/owntracks/__init__.py | 8 ++++---- homeassistant/components/sensor/arwn.py | 6 +++--- homeassistant/components/sensor/mqtt_room.py | 6 +++--- homeassistant/components/snips/__init__.py | 8 ++++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/manual_mqtt.py b/homeassistant/components/alarm_control_panel/manual_mqtt.py index 693c15fa4240c0..9bee2b81d61201 100644 --- a/homeassistant/components/alarm_control_panel/manual_mqtt.py +++ b/homeassistant/components/alarm_control_panel/manual_mqtt.py @@ -342,18 +342,18 @@ async def async_added_to_hass(self): ) @callback - def message_received(topic, payload, qos): + def message_received(msg): """Run when new MQTT message has been received.""" - if payload == self._payload_disarm: + if msg.payload == self._payload_disarm: self.async_alarm_disarm(self._code) - elif payload == self._payload_arm_home: + elif msg.payload == self._payload_arm_home: self.async_alarm_arm_home(self._code) - elif payload == self._payload_arm_away: + elif msg.payload == self._payload_arm_away: self.async_alarm_arm_away(self._code) - elif payload == self._payload_arm_night: + elif msg.payload == self._payload_arm_night: self.async_alarm_arm_night(self._code) else: - _LOGGER.warning("Received unexpected payload: %s", payload) + _LOGGER.warning("Received unexpected payload: %s", msg.payload) return await mqtt.async_subscribe( diff --git a/homeassistant/components/automation/mqtt.py b/homeassistant/components/automation/mqtt.py index 5f52da745ee14c..ff89cd47024c16 100644 --- a/homeassistant/components/automation/mqtt.py +++ b/homeassistant/components/automation/mqtt.py @@ -29,18 +29,18 @@ async def async_trigger(hass, config, action, automation_info): encoding = config[CONF_ENCODING] or None @callback - def mqtt_automation_listener(msg_topic, msg_payload, qos): + def mqtt_automation_listener(mqttmsg): """Listen for MQTT messages.""" - if payload is None or payload == msg_payload: + if payload is None or payload == mqttmsg.payload: data = { 'platform': 'mqtt', - 'topic': msg_topic, - 'payload': msg_payload, - 'qos': qos, + 'topic': mqttmsg.topic, + 'payload': mqttmsg.payload, + 'qos': mqttmsg.qos, } try: - data['payload_json'] = json.loads(msg_payload) + data['payload_json'] = json.loads(mqttmsg.payload) except ValueError: pass diff --git a/homeassistant/components/device_tracker/mqtt_json.py b/homeassistant/components/device_tracker/mqtt_json.py index 3a820d189f425a..0a1b327dca9bc3 100644 --- a/homeassistant/components/device_tracker/mqtt_json.py +++ b/homeassistant/components/device_tracker/mqtt_json.py @@ -41,17 +41,17 @@ async def async_setup_scanner(hass, config, async_see, discovery_info=None): for dev_id, topic in devices.items(): @callback - def async_message_received(topic, payload, qos, dev_id=dev_id): + def async_message_received(msg, dev_id=dev_id): """Handle received MQTT message.""" try: - data = GPS_JSON_PAYLOAD_SCHEMA(json.loads(payload)) + data = GPS_JSON_PAYLOAD_SCHEMA(json.loads(msg.payload)) except vol.MultipleInvalid: _LOGGER.error("Skipping update for following data " "because of missing or malformatted data: %s", - payload) + msg.payload) return except ValueError: - _LOGGER.error("Error parsing JSON payload: %s", payload) + _LOGGER.error("Error parsing JSON payload: %s", msg.payload) return kwargs = _parse_see_args(dev_id, data) diff --git a/homeassistant/components/mqtt_eventstream/__init__.py b/homeassistant/components/mqtt_eventstream/__init__.py index 6e545d19fe26f0..fb6a94f1870d73 100644 --- a/homeassistant/components/mqtt_eventstream/__init__.py +++ b/homeassistant/components/mqtt_eventstream/__init__.py @@ -74,9 +74,9 @@ def _event_publisher(event): # Process events from a remote server that are received on a queue. @callback - def _event_receiver(topic, payload, qos): + def _event_receiver(msg): """Receive events published by and fire them on this hass instance.""" - event = json.loads(payload) + event = json.loads(msg.payload) event_type = event.get('event_type') event_data = event.get('event_data') diff --git a/homeassistant/components/owntracks/__init__.py b/homeassistant/components/owntracks/__init__.py index c0d3d152270a35..df6b815e4c5be9 100644 --- a/homeassistant/components/owntracks/__init__.py +++ b/homeassistant/components/owntracks/__init__.py @@ -99,16 +99,16 @@ async def async_connect_mqtt(hass, component): """Subscribe to MQTT topic.""" context = hass.data[DOMAIN]['context'] - async def async_handle_mqtt_message(topic, payload, qos): + async def async_handle_mqtt_message(msg): """Handle incoming OwnTracks message.""" try: - message = json.loads(payload) + message = json.loads(msg.payload) except ValueError: # If invalid JSON - _LOGGER.error("Unable to parse payload as JSON: %s", payload) + _LOGGER.error("Unable to parse payload as JSON: %s", msg.payload) return - message['topic'] = topic + message['topic'] = msg.topic hass.helpers.dispatcher.async_dispatcher_send( DOMAIN, hass, context, message) diff --git a/homeassistant/components/sensor/arwn.py b/homeassistant/components/sensor/arwn.py index 2b79e4c3a9a87f..95825f4ca138d1 100644 --- a/homeassistant/components/sensor/arwn.py +++ b/homeassistant/components/sensor/arwn.py @@ -61,7 +61,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the ARWN platform.""" @callback - def async_sensor_event_received(topic, payload, qos): + def async_sensor_event_received(msg): """Process events as sensors. When a new event on our topic (arwn/#) is received we map it @@ -74,8 +74,8 @@ def async_sensor_event_received(topic, payload, qos): This lets us dynamically incorporate sensors without any configuration on our side. """ - event = json.loads(payload) - sensors = discover_sensors(topic, event) + event = json.loads(msg.payload) + sensors = discover_sensors(msg.topic, event) if not sensors: return diff --git a/homeassistant/components/sensor/mqtt_room.py b/homeassistant/components/sensor/mqtt_room.py index b52f039281cead..36f99719da43ba 100644 --- a/homeassistant/components/sensor/mqtt_room.py +++ b/homeassistant/components/sensor/mqtt_room.py @@ -90,16 +90,16 @@ def update_state(device_id, room, distance): self.async_schedule_update_ha_state() @callback - def message_received(topic, payload, qos): + def message_received(msg): """Handle new MQTT messages.""" try: - data = MQTT_PAYLOAD(payload) + data = MQTT_PAYLOAD(msg.payload) except vol.MultipleInvalid as error: _LOGGER.debug( "Skipping update because of malformatted data: %s", error) return - device = _parse_update_data(topic, data) + device = _parse_update_data(msg.topic, data) if device.get(CONF_DEVICE_ID) == self._device_id: if self._distance is None or self._updated is None: update_state(**device) diff --git a/homeassistant/components/snips/__init__.py b/homeassistant/components/snips/__init__.py index 20cc7137ef8803..0cc96d66b1a1aa 100644 --- a/homeassistant/components/snips/__init__.py +++ b/homeassistant/components/snips/__init__.py @@ -95,14 +95,14 @@ def async_set_feedback(site_ids, state): if CONF_FEEDBACK in config[DOMAIN]: async_set_feedback(None, config[DOMAIN][CONF_FEEDBACK]) - async def message_received(topic, payload, qos): + async def message_received(msg): """Handle new messages on MQTT.""" - _LOGGER.debug("New intent: %s", payload) + _LOGGER.debug("New intent: %s", msg.payload) try: - request = json.loads(payload) + request = json.loads(msg.payload) except TypeError: - _LOGGER.error('Received invalid JSON: %s', payload) + _LOGGER.error('Received invalid JSON: %s', msg.payload) return if (request['intent']['confidenceScore'] From 7057958e3e082a0e4a42ccfe5ed099b4c15ac396 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 14 Mar 2019 09:10:36 -0700 Subject: [PATCH 006/605] Fix lifx light async error (#22031) --- homeassistant/components/lifx/light.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index c0b6158f18609b..19a9f7583ecef9 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -24,7 +24,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.helpers.device_registry as dr from homeassistant.helpers.event import async_track_point_in_utc_time -from homeassistant.helpers.service import extract_entity_ids +from homeassistant.helpers.service import async_extract_entity_ids import homeassistant.util.color as color_util _LOGGER = logging.getLogger(__name__) @@ -250,7 +250,7 @@ def register_set_state(self): async def service_handler(service): """Apply a service.""" tasks = [] - for light in self.service_to_entities(service): + for light in await self.async_service_to_entities(service): if service.service == SERVICE_LIFX_SET_STATE: task = light.set_state(**service.data) tasks.append(self.hass.async_create_task(task)) @@ -265,7 +265,7 @@ def register_effects(self): """Register the LIFX effects as hass service calls.""" async def service_handler(service): """Apply a service, i.e. start an effect.""" - entities = self.service_to_entities(service) + entities = await self.async_service_to_entities(service) if entities: await self.start_effect( entities, service.service, **service.data) @@ -314,9 +314,9 @@ async def start_effect(self, entities, service, **kwargs): elif service == SERVICE_EFFECT_STOP: await self.effects_conductor.stop(bulbs) - def service_to_entities(self, service): + async def async_service_to_entities(self, service): """Return the known entities that a service call mentions.""" - entity_ids = extract_entity_ids(self.hass, service) + entity_ids = await async_extract_entity_ids(self.hass, service) if entity_ids: entities = [entity for entity in self.entities.values() if entity.entity_id in entity_ids] From 707d32495bad25ad345fc21798d5de970d4cf968 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 15 Mar 2019 00:18:31 +0100 Subject: [PATCH 007/605] Fix Google Assistant User with Cloud (#22042) * Fix Google Assistant User with Cloud * Fix User Agent ID * respell * Fix object * Fix tests * fix lint * Fix lint --- homeassistant/components/cloud/__init__.py | 10 +++ homeassistant/components/cloud/client.py | 12 ++-- homeassistant/components/cloud/const.py | 1 + homeassistant/components/cloud/prefs.py | 14 +++- tests/components/cloud/test_init.py | 77 ++++++++++++++++++---- 5 files changed, 94 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index c854fe69be9ca1..2e324f0673802c 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -3,6 +3,7 @@ import voluptuous as vol +from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components.alexa import smart_home as alexa_sh from homeassistant.components.google_assistant import const as ga_c from homeassistant.const import ( @@ -136,12 +137,21 @@ async def async_setup(hass, config): else: kwargs = {CONF_MODE: DEFAULT_MODE} + # Alexa/Google custom config alexa_conf = kwargs.pop(CONF_ALEXA, None) or ALEXA_SCHEMA({}) google_conf = kwargs.pop(CONF_GOOGLE_ACTIONS, None) or GACTIONS_SCHEMA({}) + # Cloud settings prefs = CloudPreferences(hass) await prefs.async_initialize() + # Cloud user + if not prefs.cloud_user: + user = await hass.auth.async_create_system_user( + 'Home Assistant Cloud', [GROUP_ID_ADMIN]) + await prefs.async_update(cloud_user=user.id) + + # Initialize Cloud websession = hass.helpers.aiohttp_client.async_get_clientsession() client = CloudClient(hass, prefs, websession, alexa_conf, google_conf) cloud = hass.data[DOMAIN] = Cloud(client, **kwargs) diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index 063a9daf00a435..f73c16b19040f5 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -136,12 +136,16 @@ async def async_google_message( if not self._prefs.google_enabled: return ga.turned_off_response(payload) - cloud = self._hass.data[DOMAIN] - return await ga.async_handle_message( - self._hass, self.google_config, - cloud.claims['cognito:username'], payload + answer = await ga.async_handle_message( + self._hass, self.google_config, self.prefs.cloud_user, payload ) + # Fix AgentUserId + cloud = self._hass.data[DOMAIN] + answer['payload']['agentUserId'] = cloud.claims['cognito:username'] + + return answer + async def async_webhook_message( self, payload: Dict[Any, Any]) -> Dict[Any, Any]: """Process cloud webhook message to client.""" diff --git a/homeassistant/components/cloud/const.py b/homeassistant/components/cloud/const.py index 65e026389f05a4..fdedacd6dbbef6 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -7,6 +7,7 @@ PREF_ENABLE_REMOTE = 'remote_enabled' PREF_GOOGLE_ALLOW_UNLOCK = 'google_allow_unlock' PREF_CLOUDHOOKS = 'cloudhooks' +PREF_CLOUD_USER = 'cloud_user' CONF_ALEXA = 'alexa' CONF_ALIASES = 'aliases' diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index 263c17935cbafe..16ff8f0c213309 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -1,7 +1,7 @@ """Preference management for cloud.""" from .const import ( DOMAIN, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, PREF_ENABLE_REMOTE, - PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS) + PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS, PREF_CLOUD_USER) STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 @@ -26,14 +26,16 @@ async def async_initialize(self): PREF_ENABLE_GOOGLE: True, PREF_ENABLE_REMOTE: False, PREF_GOOGLE_ALLOW_UNLOCK: False, - PREF_CLOUDHOOKS: {} + PREF_CLOUDHOOKS: {}, + PREF_CLOUD_USER: None, } self._prefs = prefs async def async_update(self, *, google_enabled=_UNDEF, alexa_enabled=_UNDEF, remote_enabled=_UNDEF, - google_allow_unlock=_UNDEF, cloudhooks=_UNDEF): + google_allow_unlock=_UNDEF, cloudhooks=_UNDEF, + cloud_user=_UNDEF): """Update user preferences.""" for key, value in ( (PREF_ENABLE_GOOGLE, google_enabled), @@ -41,6 +43,7 @@ async def async_update(self, *, google_enabled=_UNDEF, (PREF_ENABLE_REMOTE, remote_enabled), (PREF_GOOGLE_ALLOW_UNLOCK, google_allow_unlock), (PREF_CLOUDHOOKS, cloudhooks), + (PREF_CLOUD_USER, cloud_user), ): if value is not _UNDEF: self._prefs[key] = value @@ -75,3 +78,8 @@ def google_allow_unlock(self): def cloudhooks(self): """Return the published cloud webhooks.""" return self._prefs.get(PREF_CLOUDHOOKS, {}) + + @property + def cloud_user(self) -> str: + """Return ID from Home Assistant Cloud system user.""" + return self._prefs.get(PREF_CLOUD_USER) diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index d3e2e50f3a7d14..0de395c8bbc97d 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -1,24 +1,21 @@ """Test the cloud component.""" -from unittest.mock import MagicMock, patch +from unittest.mock import patch -from homeassistant.const import ( - EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START) +from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components import cloud from homeassistant.components.cloud.const import DOMAIN - +from homeassistant.components.cloud.prefs import STORAGE_KEY +from homeassistant.const import ( + EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) +from homeassistant.setup import async_setup_component from tests.common import mock_coro -async def test_constructor_loads_info_from_config(): +async def test_constructor_loads_info_from_config(hass): """Test non-dev mode loads info from SERVERS constant.""" - hass = MagicMock(data={}) - - with patch( - "homeassistant.components.cloud.prefs.CloudPreferences." - "async_initialize", - return_value=mock_coro() - ): - result = await cloud.async_setup(hass, { + with patch("hass_nabucasa.Cloud.start", return_value=mock_coro()): + result = await async_setup_component(hass, 'cloud', { + 'http': {}, 'cloud': { cloud.CONF_MODE: cloud.MODE_DEV, 'cognito_client_id': 'test-cognito_client_id', @@ -79,3 +76,57 @@ async def test_startup_shutdown_events(hass, mock_cloud_fixture): await hass.async_block_till_done() assert mock_stop.called + + +async def test_setup_existing_cloud_user(hass, hass_storage): + """Test setup with API push default data.""" + user = await hass.auth.async_create_system_user('Cloud test') + hass_storage[STORAGE_KEY] = { + 'version': 1, + 'data': { + 'cloud_user': user.id + } + } + with patch('hass_nabucasa.Cloud.start', return_value=mock_coro()): + result = await async_setup_component(hass, 'cloud', { + 'http': {}, + 'cloud': { + cloud.CONF_MODE: cloud.MODE_DEV, + 'cognito_client_id': 'test-cognito_client_id', + 'user_pool_id': 'test-user_pool_id', + 'region': 'test-region', + 'relayer': 'test-relayer', + } + }) + assert result + + assert hass_storage[STORAGE_KEY]['data']['cloud_user'] == user.id + + +async def test_setup_setup_cloud_user(hass, hass_storage): + """Test setup with API push default data.""" + hass_storage[STORAGE_KEY] = { + 'version': 1, + 'data': { + 'cloud_user': None + } + } + with patch('hass_nabucasa.Cloud.start', return_value=mock_coro()): + result = await async_setup_component(hass, 'cloud', { + 'http': {}, + 'cloud': { + cloud.CONF_MODE: cloud.MODE_DEV, + 'cognito_client_id': 'test-cognito_client_id', + 'user_pool_id': 'test-user_pool_id', + 'region': 'test-region', + 'relayer': 'test-relayer', + } + }) + assert result + + cloud_user = await hass.auth.async_get_user( + hass_storage[STORAGE_KEY]['data']['cloud_user'] + ) + + assert cloud_user + assert cloud_user.groups[0].id == GROUP_ID_ADMIN From fb895bba804927da756b5a48f67ed6f03ed6f3ca Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 14 Mar 2019 16:25:00 -0700 Subject: [PATCH 008/605] Bumped version to 0.90.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7b74c25d08ac74..b1a0fbb1e4f358 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b0' +PATCH_VERSION = '0b1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 9e6a7a635797d127575debf238abf8f2a8212d9a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 10:43:32 -0700 Subject: [PATCH 009/605] Updated frontend to 20190315.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 70c2bab082997a..a8d2cbc35b9dc9 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190313.0'] +REQUIREMENTS = ['home-assistant-frontend==20190315.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index c930723d00b081..9200b803b8f391 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190313.0 +home-assistant-frontend==20190315.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f7f1b77559a072..36f94167565a8e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190313.0 +home-assistant-frontend==20190315.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From b336322e9eac684230b256e2c310013c7389e512 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:22:43 -0700 Subject: [PATCH 010/605] Mobile App: Require encryption for registrations that support it (#21852) ## Description: **Related issue (if applicable):** fixes #21758 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/mobile_app/const.py | 1 + .../components/mobile_app/webhook.py | 25 ++++++++++++------- tests/components/mobile_app/test_webhook.py | 15 +++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 60b4cde4708659..11b6f3e9865fb6 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -41,6 +41,7 @@ ATTR_WEBHOOK_ENCRYPTED_DATA = 'encrypted_data' ATTR_WEBHOOK_TYPE = 'type' +ERR_ENCRYPTION_REQUIRED = 'encryption_required' ERR_INVALID_COMPONENT = 'invalid_component' ERR_RENDER_FAILURE = 'render_failure' ERR_SAVE_FAILURE = 'save_failure' diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 61188b50e1b7db..9efd1fcd9f8feb 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -20,15 +20,16 @@ from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import HomeAssistantType -from .const import (ATTR_APP_COMPONENT, DATA_DELETED_IDS, - ATTR_DEVICE_NAME, ATTR_EVENT_DATA, ATTR_EVENT_TYPE, - DATA_REGISTRATIONS, ATTR_TEMPLATE, ATTR_TEMPLATE_VARIABLES, - ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, - ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, - CONF_SECRET, DOMAIN, ERR_RENDER_FAILURE, - WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, - WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, +from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_NAME, ATTR_EVENT_DATA, + ATTR_EVENT_TYPE, ATTR_SUPPORTS_ENCRYPTION, ATTR_TEMPLATE, + ATTR_TEMPLATE_VARIABLES, ATTR_WEBHOOK_DATA, + ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, + ATTR_WEBHOOK_TYPE, CONF_SECRET, DATA_DELETED_IDS, + DATA_REGISTRATIONS, DOMAIN, ERR_ENCRYPTION_REQUIRED, + ERR_RENDER_FAILURE, WEBHOOK_PAYLOAD_SCHEMA, + WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION) from .helpers import (_decrypt_payload, empty_okay_response, error_response, @@ -78,6 +79,12 @@ async def handle_webhook(store: Store, hass: HomeAssistantType, _LOGGER.warning('Received invalid JSON from mobile_app') return empty_okay_response(status=HTTP_BAD_REQUEST) + if (ATTR_WEBHOOK_ENCRYPTED not in req_data and + registration[ATTR_SUPPORTS_ENCRYPTION]): + _LOGGER.warning("Refusing to accept unencrypted webhook from %s", + registration[ATTR_DEVICE_NAME]) + return error_response(ERR_ENCRYPTION_REQUIRED, "Encryption required") + try: req_data = WEBHOOK_PAYLOAD_SCHEMA(req_data) except vol.Invalid as ex: diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index a935110754c923..bbdfcde93e731e 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -149,3 +149,18 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 decrypted_data = decrypted_data.decode("utf-8") assert json.loads(decrypted_data) == {'rendered': 'Hello world'} + + +async def test_webhook_requires_encryption(webhook_client): # noqa: F811 + """Test that encrypted registrations only accept encrypted data.""" + resp = await webhook_client.post( + '/api/webhook/mobile_app_test', + json=RENDER_TEMPLATE + ) + + assert resp.status == 400 + + webhook_json = await resp.json() + assert 'error' in webhook_json + assert webhook_json['success'] is False + assert webhook_json['error']['code'] == 'encryption_required' From c67113ad55652da1eee9231c7b5105dd7918b021 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:33:37 -0700 Subject: [PATCH 011/605] Mobile App: Support rendering multiple templates at once (#21851) * Support rendering multiple templates at once * Only catch TemplateError and dont log the error --- homeassistant/components/mobile_app/const.py | 7 ++-- .../components/mobile_app/webhook.py | 32 +++++++++---------- tests/components/mobile_app/const.py | 4 ++- tests/components/mobile_app/test_http_api.py | 6 ++-- tests/components/mobile_app/test_webhook.py | 6 ++-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 11b6f3e9865fb6..3f1a6bc988c670 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -43,7 +43,6 @@ ERR_ENCRYPTION_REQUIRED = 'encryption_required' ERR_INVALID_COMPONENT = 'invalid_component' -ERR_RENDER_FAILURE = 'render_failure' ERR_SAVE_FAILURE = 'save_failure' WEBHOOK_TYPE_CALL_SERVICE = 'call_service' @@ -99,8 +98,10 @@ }) RENDER_TEMPLATE_SCHEMA = vol.Schema({ - vol.Required(ATTR_TEMPLATE): cv.string, - vol.Optional(ATTR_TEMPLATE_VARIABLES, default={}): dict, + str: { + vol.Required(ATTR_TEMPLATE): cv.template, + vol.Optional(ATTR_TEMPLATE_VARIABLES, default={}): dict, + } }) WEBHOOK_SCHEMAS = { diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 9efd1fcd9f8feb..e8372c8648d93f 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -15,7 +15,7 @@ from homeassistant.core import EventOrigin from homeassistant.exceptions import (HomeAssistantError, ServiceNotFound, TemplateError) -from homeassistant.helpers import template +from homeassistant.helpers.template import attach from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import HomeAssistantType @@ -26,10 +26,9 @@ ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, CONF_SECRET, DATA_DELETED_IDS, DATA_REGISTRATIONS, DOMAIN, ERR_ENCRYPTION_REQUIRED, - ERR_RENDER_FAILURE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, + WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION) from .helpers import (_decrypt_payload, empty_okay_response, error_response, @@ -132,17 +131,18 @@ async def handle_webhook(store: Store, hass: HomeAssistantType, return empty_okay_response(headers=headers) if webhook_type == WEBHOOK_TYPE_RENDER_TEMPLATE: - try: - tpl = template.Template(data[ATTR_TEMPLATE], hass) - rendered = tpl.async_render(data.get(ATTR_TEMPLATE_VARIABLES)) - return webhook_response({"rendered": rendered}, - registration=registration, headers=headers) - # noqa: E722 pylint: disable=broad-except - except (ValueError, TemplateError, Exception) as ex: - _LOGGER.error("Error when rendering template during mobile_app " - "webhook (device name: %s): %s", - registration[ATTR_DEVICE_NAME], ex) - return error_response(ERR_RENDER_FAILURE, str(ex), headers=headers) + resp = {} + for key, item in data.items(): + try: + tpl = item[ATTR_TEMPLATE] + attach(hass, tpl) + resp[key] = tpl.async_render(item.get(ATTR_TEMPLATE_VARIABLES)) + # noqa: E722 pylint: disable=broad-except + except TemplateError as ex: + resp[key] = {"error": str(ex)} + + return webhook_response(resp, registration=registration, + headers=headers) if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION: try: diff --git a/tests/components/mobile_app/const.py b/tests/components/mobile_app/const.py index 919a2a6e1fbeae..6dfe050191b3b8 100644 --- a/tests/components/mobile_app/const.py +++ b/tests/components/mobile_app/const.py @@ -49,7 +49,9 @@ RENDER_TEMPLATE = { 'type': 'render_template', 'data': { - 'template': 'Hello world' + 'one': { + 'template': 'Hello world' + } } } diff --git a/tests/components/mobile_app/test_http_api.py b/tests/components/mobile_app/test_http_api.py index 195d33e15b2005..7861e63459a32b 100644 --- a/tests/components/mobile_app/test_http_api.py +++ b/tests/components/mobile_app/test_http_api.py @@ -5,7 +5,7 @@ from homeassistant.components.mobile_app.const import CONF_SECRET from homeassistant.const import CONF_WEBHOOK_ID -from .const import REGISTER +from .const import REGISTER, RENDER_TEMPLATE from . import authed_api_client # noqa: F401 @@ -35,7 +35,7 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811 key = key[:keylen] key = key.ljust(keylen, b'\0') - payload = json.dumps({'template': 'Hello world'}).encode("utf-8") + payload = json.dumps(RENDER_TEMPLATE['data']).encode("utf-8") data = SecretBox(key).encrypt(payload, encoder=Base64Encoder).decode("utf-8") @@ -62,7 +62,7 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811 encoder=Base64Encoder) decrypted_data = decrypted_data.decode("utf-8") - assert json.loads(decrypted_data) == {'rendered': 'Hello world'} + assert json.loads(decrypted_data) == {'one': 'Hello world'} async def test_register_invalid_component(authed_api_client): # noqa: F811 diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index bbdfcde93e731e..75e8903c494524 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -24,7 +24,7 @@ async def test_webhook_handle_render_template(webhook_client): # noqa: F811 assert resp.status == 200 json = await resp.json() - assert json == {'rendered': 'Hello world'} + assert json == {'one': 'Hello world'} async def test_webhook_handle_call_services(hass, webhook_client): # noqa: E501 F811 @@ -123,7 +123,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 key = key[:keylen] key = key.ljust(keylen, b'\0') - payload = json.dumps({'template': 'Hello world'}).encode("utf-8") + payload = json.dumps(RENDER_TEMPLATE['data']).encode("utf-8") data = SecretBox(key).encrypt(payload, encoder=Base64Encoder).decode("utf-8") @@ -148,7 +148,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 encoder=Base64Encoder) decrypted_data = decrypted_data.decode("utf-8") - assert json.loads(decrypted_data) == {'rendered': 'Hello world'} + assert json.loads(decrypted_data) == {'one': 'Hello world'} async def test_webhook_requires_encryption(webhook_client): # noqa: F811 From 3fd1e8d38285b8ca491e6982576ae1eefc8ddaca Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 15:38:53 -0700 Subject: [PATCH 012/605] Mobile App: Update Location schema updates & device ID generation (#21849) * Update location schema * Generate a random device ID at registration time for later use with device_tracker.see * Remove host name from device_tracker.see payload * Drop consider_home from the payload * Remove stale consider_home in schema * Remove source_type --- homeassistant/components/mobile_app/const.py | 24 ++++++++++- .../components/mobile_app/http_api.py | 12 ++++-- .../components/mobile_app/webhook.py | 42 ++++++++++++++----- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 3f1a6bc988c670..7a497d7645490e 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -1,7 +1,10 @@ """Constants for mobile_app.""" import voluptuous as vol -from homeassistant.components.device_tracker import SERVICE_SEE_PAYLOAD_SCHEMA +from homeassistant.components.device_tracker import (ATTR_BATTERY, + ATTR_GPS, + ATTR_GPS_ACCURACY, + ATTR_LOCATION_NAME) from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA) from homeassistant.helpers import config_validation as cv @@ -23,6 +26,7 @@ ATTR_APP_ID = 'app_id' ATTR_APP_NAME = 'app_name' ATTR_APP_VERSION = 'app_version' +ATTR_DEVICE_ID = 'device_id' ATTR_DEVICE_NAME = 'device_name' ATTR_MANUFACTURER = 'manufacturer' ATTR_MODEL = 'model' @@ -36,6 +40,11 @@ ATTR_TEMPLATE = 'template' ATTR_TEMPLATE_VARIABLES = 'variables' +ATTR_SPEED = 'speed' +ATTR_ALTITUDE = 'altitude' +ATTR_COURSE = 'course' +ATTR_VERTICAL_ACCURACY = 'vertical_accuracy' + ATTR_WEBHOOK_DATA = 'data' ATTR_WEBHOOK_ENCRYPTED = 'encrypted' ATTR_WEBHOOK_ENCRYPTED_DATA = 'encrypted_data' @@ -104,10 +113,21 @@ } }) +UPDATE_LOCATION_SCHEMA = vol.Schema({ + vol.Optional(ATTR_LOCATION_NAME): cv.string, + vol.Required(ATTR_GPS): cv.gps, + vol.Required(ATTR_GPS_ACCURACY): cv.positive_int, + vol.Optional(ATTR_BATTERY): cv.positive_int, + vol.Optional(ATTR_SPEED): cv.positive_int, + vol.Optional(ATTR_ALTITUDE): cv.positive_int, + vol.Optional(ATTR_COURSE): cv.positive_int, + vol.Optional(ATTR_VERTICAL_ACCURACY): cv.positive_int, +}) + WEBHOOK_SCHEMAS = { WEBHOOK_TYPE_CALL_SERVICE: CALL_SERVICE_SCHEMA, WEBHOOK_TYPE_FIRE_EVENT: FIRE_EVENT_SCHEMA, WEBHOOK_TYPE_RENDER_TEMPLATE: RENDER_TEMPLATE_SCHEMA, - WEBHOOK_TYPE_UPDATE_LOCATION: SERVICE_SEE_PAYLOAD_SCHEMA, + WEBHOOK_TYPE_UPDATE_LOCATION: UPDATE_LOCATION_SCHEMA, WEBHOOK_TYPE_UPDATE_REGISTRATION: UPDATE_REGISTRATION_SCHEMA, } diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index 30083cc86b1ab0..4948407b63b71d 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -1,4 +1,5 @@ """Provides an HTTP API for mobile_app.""" +import uuid from typing import Dict from aiohttp.web import Response, Request @@ -15,10 +16,11 @@ from homeassistant.helpers.typing import HomeAssistantType from homeassistant.loader import get_component -from .const import (ATTR_APP_COMPONENT, ATTR_SUPPORTS_ENCRYPTION, - CONF_CLOUDHOOK_URL, CONF_SECRET, CONF_USER_ID, - DATA_REGISTRATIONS, DOMAIN, ERR_INVALID_COMPONENT, - ERR_SAVE_FAILURE, REGISTRATION_SCHEMA) +from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, + ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, CONF_SECRET, + CONF_USER_ID, DATA_REGISTRATIONS, DOMAIN, + ERR_INVALID_COMPONENT, ERR_SAVE_FAILURE, + REGISTRATION_SCHEMA) from .helpers import error_response, supports_encryption, savable_state @@ -66,6 +68,8 @@ async def post(self, request: Request, data: Dict) -> Response: data[CONF_CLOUDHOOK_URL] = \ await async_create_cloudhook(hass, webhook_id) + data[ATTR_DEVICE_ID] = str(uuid.uuid4()).replace("-", "") + data[CONF_WEBHOOK_ID] = webhook_id if data[ATTR_SUPPORTS_ENCRYPTION] and supports_encryption(): diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index e8372c8648d93f..4d3e0aef4c687d 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -6,7 +6,9 @@ from aiohttp.web import HTTPBadRequest, Response, Request import voluptuous as vol -from homeassistant.components.device_tracker import (DOMAIN as DT_DOMAIN, +from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES, + ATTR_DEV_ID, + DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) from homeassistant.components.webhook import async_register as webhook_register @@ -20,15 +22,19 @@ from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import HomeAssistantType -from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_NAME, ATTR_EVENT_DATA, - ATTR_EVENT_TYPE, ATTR_SUPPORTS_ENCRYPTION, ATTR_TEMPLATE, - ATTR_TEMPLATE_VARIABLES, ATTR_WEBHOOK_DATA, - ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, - ATTR_WEBHOOK_TYPE, CONF_SECRET, DATA_DELETED_IDS, - DATA_REGISTRATIONS, DOMAIN, ERR_ENCRYPTION_REQUIRED, - WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, - WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, +from .const import (ATTR_ALTITUDE, ATTR_APP_COMPONENT, ATTR_BATTERY, + ATTR_COURSE, ATTR_DEVICE_ID, ATTR_DEVICE_NAME, + ATTR_EVENT_DATA, ATTR_EVENT_TYPE, ATTR_GPS, + ATTR_GPS_ACCURACY, ATTR_LOCATION_NAME, ATTR_SPEED, + ATTR_SUPPORTS_ENCRYPTION, ATTR_TEMPLATE, + ATTR_TEMPLATE_VARIABLES, ATTR_VERTICAL_ACCURACY, + ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, + ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, + CONF_SECRET, DATA_DELETED_IDS, DATA_REGISTRATIONS, DOMAIN, + ERR_ENCRYPTION_REQUIRED, WEBHOOK_PAYLOAD_SCHEMA, + WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION) from .helpers import (_decrypt_payload, empty_okay_response, error_response, @@ -145,9 +151,23 @@ async def handle_webhook(store: Store, hass: HomeAssistantType, headers=headers) if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION: + see_payload = { + ATTR_DEV_ID: registration[ATTR_DEVICE_ID], + ATTR_LOCATION_NAME: data.get(ATTR_LOCATION_NAME), + ATTR_GPS: data.get(ATTR_GPS), + ATTR_GPS_ACCURACY: data.get(ATTR_GPS_ACCURACY), + ATTR_BATTERY: data.get(ATTR_BATTERY), + ATTR_ATTRIBUTES: { + ATTR_SPEED: data.get(ATTR_SPEED), + ATTR_ALTITUDE: data.get(ATTR_ALTITUDE), + ATTR_COURSE: data.get(ATTR_COURSE), + ATTR_VERTICAL_ACCURACY: data.get(ATTR_VERTICAL_ACCURACY), + } + } + try: await hass.services.async_call(DT_DOMAIN, - DT_SEE, data, + DT_SEE, see_payload, blocking=True, context=context) # noqa: E722 pylint: disable=broad-except except (vol.Invalid, ServiceNotFound, Exception) as ex: From f7dcfe28b680db4b60cc36ba50b2c7c94f8648f9 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 12:57:50 -0700 Subject: [PATCH 013/605] Mobile App: Register devices into the registry (#21856) * Register devices into the registry * Switch to device ID instead of webhook ID * Rearchitect mobile_app to support config entries * Kill DATA_REGISTRATIONS by migrating registrations into config entries * Fix tests * Improve how we get the config_entry_id * Remove single_instance_allowed * Simplify setup_registration * Move webhook registering functions into __init__.py since they are only ever used once * Kill get_registration websocket command * Support description_placeholders in async_abort * Add link to mobile_app implementing apps in abort dialog * Store config entry and device registry entry in hass.data instead of looking it up * Add testing to ensure that the config entry is created at registration * Fix busted async_abort test * Remove unnecessary check for entry is None --- .../mobile_app/.translations/en.json | 14 +++ .../components/mobile_app/__init__.py | 103 +++++++++++++++--- homeassistant/components/mobile_app/const.py | 5 +- .../components/mobile_app/helpers.py | 4 +- .../components/mobile_app/http_api.py | 37 ++----- .../components/mobile_app/strings.json | 14 +++ .../components/mobile_app/webhook.py | 75 +++++-------- .../components/mobile_app/websocket_api.py | 48 ++------ homeassistant/config_entries.py | 1 + homeassistant/data_entry_flow.py | 6 +- .../components/config/test_config_entries.py | 1 + tests/components/mobile_app/__init__.py | 65 ++++++----- tests/components/mobile_app/test_http_api.py | 29 ++++- tests/components/mobile_app/test_webhook.py | 39 ++++--- .../mobile_app/test_websocket_api.py | 27 ----- 15 files changed, 255 insertions(+), 213 deletions(-) create mode 100644 homeassistant/components/mobile_app/.translations/en.json create mode 100644 homeassistant/components/mobile_app/strings.json diff --git a/homeassistant/components/mobile_app/.translations/en.json b/homeassistant/components/mobile_app/.translations/en.json new file mode 100644 index 00000000000000..646151a522909a --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/en.json @@ -0,0 +1,14 @@ +{ + "config": { + "title": "Mobile App", + "step": { + "confirm": { + "title": "Mobile App", + "description": "Do you want to set up the Mobile App component?" + } + }, + "abort": { + "install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps." + } + } +} diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index 0d95bfe68320b8..1c348ea078265e 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -1,11 +1,18 @@ """Integrates Native Apps to Home Assistant.""" +from homeassistant import config_entries +from homeassistant.const import CONF_WEBHOOK_ID +from homeassistant.components.webhook import async_register as webhook_register +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from .const import (DATA_DELETED_IDS, DATA_REGISTRATIONS, DATA_STORE, DOMAIN, - STORAGE_KEY, STORAGE_VERSION) +from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, ATTR_DEVICE_NAME, + ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, + DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_DEVICES, + DATA_STORE, DOMAIN, STORAGE_KEY, STORAGE_VERSION) -from .http_api import register_http_handlers -from .webhook import register_deleted_webhooks, setup_registration +from .http_api import RegistrationsView +from .webhook import handle_webhook from .websocket_api import register_websocket_handlers DEPENDENCIES = ['device_tracker', 'http', 'webhook'] @@ -15,24 +22,88 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the mobile app component.""" + hass.data[DOMAIN] = { + DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, + } + store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) app_config = await store.async_load() if app_config is None: - app_config = {DATA_DELETED_IDS: [], DATA_REGISTRATIONS: {}} - - if hass.data.get(DOMAIN) is None: - hass.data[DOMAIN] = {DATA_DELETED_IDS: [], DATA_REGISTRATIONS: {}} + app_config = { + DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, + } - hass.data[DOMAIN][DATA_DELETED_IDS] = app_config.get(DATA_DELETED_IDS, []) - hass.data[DOMAIN][DATA_REGISTRATIONS] = app_config.get(DATA_REGISTRATIONS, - {}) + hass.data[DOMAIN] = app_config hass.data[DOMAIN][DATA_STORE] = store - for registration in app_config[DATA_REGISTRATIONS].values(): - setup_registration(hass, store, registration) - - register_http_handlers(hass, store) + hass.http.register_view(RegistrationsView()) register_websocket_handlers(hass) - register_deleted_webhooks(hass, store) + + for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: + try: + webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, + handle_webhook) + except ValueError: + pass + + return True + + +async def async_setup_entry(hass, entry): + """Set up a mobile_app entry.""" + registration = entry.data + + webhook_id = registration[CONF_WEBHOOK_ID] + + hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] = entry + + device_registry = await dr.async_get_registry(hass) + + identifiers = { + (ATTR_DEVICE_ID, registration[ATTR_DEVICE_ID]), + (CONF_WEBHOOK_ID, registration[CONF_WEBHOOK_ID]) + } + + device = device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers=identifiers, + manufacturer=registration[ATTR_MANUFACTURER], + model=registration[ATTR_MODEL], + name=registration[ATTR_DEVICE_NAME], + sw_version=registration[ATTR_OS_VERSION] + ) + + hass.data[DOMAIN][DATA_DEVICES][webhook_id] = device + + registration_name = 'Mobile App: {}'.format(registration[ATTR_DEVICE_NAME]) + webhook_register(hass, DOMAIN, registration_name, webhook_id, + handle_webhook) + + if ATTR_APP_COMPONENT in registration: + load_platform(hass, registration[ATTR_APP_COMPONENT], DOMAIN, {}, + {DOMAIN: {}}) return True + + +@config_entries.HANDLERS.register(DOMAIN) +class MobileAppFlowHandler(config_entries.ConfigFlow): + """Handle a Mobile App config flow.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH + + async def async_step_user(self, user_input=None): + """Handle a flow initialized by the user.""" + placeholders = { + 'apps_url': + 'https://www.home-assistant.io/components/mobile_app/#apps' + } + + return self.async_abort(reason='install_app', + description_placeholders=placeholders) + + async def async_step_registration(self, user_input=None): + """Handle a flow initialized during registration.""" + return self.async_create_entry(title=user_input[ATTR_DEVICE_NAME], + data=user_input) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 7a497d7645490e..3ba029fec0e96a 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -17,8 +17,9 @@ CONF_SECRET = 'secret' CONF_USER_ID = 'user_id' +DATA_CONFIG_ENTRIES = 'config_entries' DATA_DELETED_IDS = 'deleted_ids' -DATA_REGISTRATIONS = 'registrations' +DATA_DEVICES = 'devices' DATA_STORE = 'store' ATTR_APP_COMPONENT = 'app_component' @@ -26,6 +27,7 @@ ATTR_APP_ID = 'app_id' ATTR_APP_NAME = 'app_name' ATTR_APP_VERSION = 'app_version' +ATTR_CONFIG_ENTRY_ID = 'entry_id' ATTR_DEVICE_ID = 'device_id' ATTR_DEVICE_NAME = 'device_name' ATTR_MANUFACTURER = 'manufacturer' @@ -52,7 +54,6 @@ ERR_ENCRYPTION_REQUIRED = 'encryption_required' ERR_INVALID_COMPONENT = 'invalid_component' -ERR_SAVE_FAILURE = 'save_failure' WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index 1f67170a72ca3f..5ec3b99b2917e5 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -11,8 +11,7 @@ from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME, ATTR_APP_VERSION, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, ATTR_SUPPORTS_ENCRYPTION, - CONF_SECRET, CONF_USER_ID, DATA_DELETED_IDS, - DATA_REGISTRATIONS, DOMAIN) + CONF_SECRET, CONF_USER_ID, DATA_DELETED_IDS, DOMAIN) _LOGGER = logging.getLogger(__name__) @@ -125,7 +124,6 @@ def savable_state(hass: HomeAssistantType) -> Dict: """Return a clean object containing things that should be saved.""" return { DATA_DELETED_IDS: hass.data[DOMAIN][DATA_DELETED_IDS], - DATA_REGISTRATIONS: hass.data[DOMAIN][DATA_REGISTRATIONS] } diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index 4948407b63b71d..8076d217cac47d 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -8,29 +8,16 @@ from homeassistant.components.cloud import async_create_cloudhook from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.data_validator import RequestDataValidator -from homeassistant.const import (HTTP_CREATED, HTTP_INTERNAL_SERVER_ERROR, - CONF_WEBHOOK_ID) +from homeassistant.const import (HTTP_CREATED, CONF_WEBHOOK_ID) -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.storage import Store -from homeassistant.helpers.typing import HomeAssistantType from homeassistant.loader import get_component from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, CONF_SECRET, - CONF_USER_ID, DATA_REGISTRATIONS, DOMAIN, - ERR_INVALID_COMPONENT, ERR_SAVE_FAILURE, + CONF_USER_ID, DOMAIN, ERR_INVALID_COMPONENT, REGISTRATION_SCHEMA) -from .helpers import error_response, supports_encryption, savable_state - -from .webhook import setup_registration - - -def register_http_handlers(hass: HomeAssistantType, store: Store) -> bool: - """Register the HTTP handlers/views.""" - hass.http.register_view(RegistrationsView(store)) - return True +from .helpers import error_response, supports_encryption class RegistrationsView(HomeAssistantView): @@ -39,10 +26,6 @@ class RegistrationsView(HomeAssistantView): url = '/api/mobile_app/registrations' name = 'api:mobile_app:register' - def __init__(self, store: Store) -> None: - """Initialize the view.""" - self._store = store - @RequestDataValidator(REGISTRATION_SCHEMA) async def post(self, request: Request, data: Dict) -> Response: """Handle the POST request for registration.""" @@ -79,16 +62,10 @@ async def post(self, request: Request, data: Dict) -> Response: data[CONF_USER_ID] = request['hass_user'].id - hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] = data - - try: - await self._store.async_save(savable_state(hass)) - except HomeAssistantError: - return error_response(ERR_SAVE_FAILURE, - "Error saving registration", - status=HTTP_INTERNAL_SERVER_ERROR) - - setup_registration(hass, self._store, data) + ctx = {'source': 'registration'} + await hass.async_create_task( + hass.config_entries.flow.async_init(DOMAIN, context=ctx, + data=data)) return self.json({ CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL), diff --git a/homeassistant/components/mobile_app/strings.json b/homeassistant/components/mobile_app/strings.json new file mode 100644 index 00000000000000..646151a522909a --- /dev/null +++ b/homeassistant/components/mobile_app/strings.json @@ -0,0 +1,14 @@ +{ + "config": { + "title": "Mobile App", + "step": { + "confirm": { + "title": "Mobile App", + "description": "Do you want to set up the Mobile App component?" + } + }, + "abort": { + "install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps." + } + } +} diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 4d3e0aef4c687d..1fab29160b7d24 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -1,7 +1,5 @@ """Webhook handlers for mobile_app.""" -from functools import partial import logging -from typing import Dict from aiohttp.web import HTTPBadRequest, Response, Request import voluptuous as vol @@ -10,27 +8,24 @@ ATTR_DEV_ID, DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) -from homeassistant.components.webhook import async_register as webhook_register from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, CONF_WEBHOOK_ID, HTTP_BAD_REQUEST) from homeassistant.core import EventOrigin -from homeassistant.exceptions import (HomeAssistantError, ServiceNotFound, - TemplateError) +from homeassistant.exceptions import (ServiceNotFound, TemplateError) +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.template import attach -from homeassistant.helpers.discovery import load_platform -from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import HomeAssistantType -from .const import (ATTR_ALTITUDE, ATTR_APP_COMPONENT, ATTR_BATTERY, - ATTR_COURSE, ATTR_DEVICE_ID, ATTR_DEVICE_NAME, - ATTR_EVENT_DATA, ATTR_EVENT_TYPE, ATTR_GPS, - ATTR_GPS_ACCURACY, ATTR_LOCATION_NAME, ATTR_SPEED, +from .const import (ATTR_ALTITUDE, ATTR_BATTERY, ATTR_COURSE, ATTR_DEVICE_ID, + ATTR_DEVICE_NAME, ATTR_EVENT_DATA, ATTR_EVENT_TYPE, + ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_LOCATION_NAME, + ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, ATTR_SPEED, ATTR_SUPPORTS_ENCRYPTION, ATTR_TEMPLATE, ATTR_TEMPLATE_VARIABLES, ATTR_VERTICAL_ACCURACY, ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, - CONF_SECRET, DATA_DELETED_IDS, DATA_REGISTRATIONS, DOMAIN, + CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DOMAIN, ERR_ENCRYPTION_REQUIRED, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, @@ -38,45 +33,24 @@ WEBHOOK_TYPE_UPDATE_REGISTRATION) from .helpers import (_decrypt_payload, empty_okay_response, error_response, - registration_context, safe_registration, savable_state, + registration_context, safe_registration, webhook_response) _LOGGER = logging.getLogger(__name__) -def register_deleted_webhooks(hass: HomeAssistantType, store: Store): - """Register previously deleted webhook IDs so we can return 410.""" - for deleted_id in hass.data[DOMAIN][DATA_DELETED_IDS]: - try: - webhook_register(hass, DOMAIN, "Deleted Webhook", deleted_id, - partial(handle_webhook, store)) - except ValueError: - pass - - -def setup_registration(hass: HomeAssistantType, store: Store, - registration: Dict) -> None: - """Register the webhook for a registration and loads the app component.""" - registration_name = 'Mobile App: {}'.format(registration[ATTR_DEVICE_NAME]) - webhook_id = registration[CONF_WEBHOOK_ID] - webhook_register(hass, DOMAIN, registration_name, webhook_id, - partial(handle_webhook, store)) - - if ATTR_APP_COMPONENT in registration: - load_platform(hass, registration[ATTR_APP_COMPONENT], DOMAIN, {}, - {DOMAIN: {}}) - - -async def handle_webhook(store: Store, hass: HomeAssistantType, - webhook_id: str, request: Request) -> Response: +async def handle_webhook(hass: HomeAssistantType, webhook_id: str, + request: Request) -> Response: """Handle webhook callback.""" if webhook_id in hass.data[DOMAIN][DATA_DELETED_IDS]: return Response(status=410) headers = {} - registration = hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] + config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] + + registration = config_entry.data try: req_data = await request.json() @@ -179,13 +153,22 @@ async def handle_webhook(store: Store, hass: HomeAssistantType, if webhook_type == WEBHOOK_TYPE_UPDATE_REGISTRATION: new_registration = {**registration, **data} - hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] = new_registration - - try: - await store.async_save(savable_state(hass)) - except HomeAssistantError as ex: - _LOGGER.error("Error updating mobile_app registration: %s", ex) - return empty_okay_response() + device_registry = await dr.async_get_registry(hass) + + device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers={ + (ATTR_DEVICE_ID, registration[ATTR_DEVICE_ID]), + (CONF_WEBHOOK_ID, registration[CONF_WEBHOOK_ID]) + }, + manufacturer=new_registration[ATTR_MANUFACTURER], + model=new_registration[ATTR_MODEL], + name=new_registration[ATTR_DEVICE_NAME], + sw_version=new_registration[ATTR_OS_VERSION] + ) + + hass.config_entries.async_update_entry(config_entry, + data=new_registration) return webhook_response(safe_registration(new_registration), registration=registration, headers=headers) diff --git a/homeassistant/components/mobile_app/websocket_api.py b/homeassistant/components/mobile_app/websocket_api.py index 5f6a25cbcec771..7bc1e59d623b42 100644 --- a/homeassistant/components/mobile_app/websocket_api.py +++ b/homeassistant/components/mobile_app/websocket_api.py @@ -17,16 +17,14 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import HomeAssistantType -from .const import (CONF_CLOUDHOOK_URL, CONF_USER_ID, DATA_DELETED_IDS, - DATA_REGISTRATIONS, DATA_STORE, DOMAIN) +from .const import (CONF_CLOUDHOOK_URL, CONF_USER_ID, DATA_CONFIG_ENTRIES, + DATA_DELETED_IDS, DATA_STORE, DOMAIN) from .helpers import safe_registration, savable_state def register_websocket_handlers(hass: HomeAssistantType) -> bool: """Register the websocket handlers.""" - async_register_command(hass, websocket_get_registration) - async_register_command(hass, websocket_get_user_registrations) async_register_command(hass, websocket_delete_registration) @@ -34,39 +32,6 @@ def register_websocket_handlers(hass: HomeAssistantType) -> bool: return True -@ws_require_user() -@async_response -@websocket_command({ - vol.Required('type'): 'mobile_app/get_registration', - vol.Required(CONF_WEBHOOK_ID): cv.string, -}) -async def websocket_get_registration( - hass: HomeAssistantType, connection: ActiveConnection, - msg: dict) -> None: - """Return the registration for the given webhook_id.""" - user = connection.user - - webhook_id = msg.get(CONF_WEBHOOK_ID) - if webhook_id is None: - connection.send_error(msg['id'], ERR_INVALID_FORMAT, - "Webhook ID not provided") - return - - registration = hass.data[DOMAIN][DATA_REGISTRATIONS].get(webhook_id) - - if registration is None: - connection.send_error(msg['id'], ERR_NOT_FOUND, - "Webhook ID not found in storage") - return - - if registration[CONF_USER_ID] != user.id and not user.is_admin: - return error_message( - msg['id'], ERR_UNAUTHORIZED, 'User is not registration owner') - - connection.send_message( - result_message(msg['id'], safe_registration(registration))) - - @ws_require_user() @async_response @websocket_command({ @@ -87,7 +52,8 @@ async def websocket_get_user_registrations( user_registrations = [] - for registration in hass.data[DOMAIN][DATA_REGISTRATIONS].values(): + for config_entry in hass.config_entries.async_entries(domain=DOMAIN): + registration = config_entry.data if connection.user.is_admin or registration[CONF_USER_ID] is user_id: user_registrations.append(safe_registration(registration)) @@ -113,7 +79,9 @@ async def websocket_delete_registration(hass: HomeAssistantType, "Webhook ID not provided") return - registration = hass.data[DOMAIN][DATA_REGISTRATIONS].get(webhook_id) + config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] + + registration = config_entry.data if registration is None: connection.send_error(msg['id'], ERR_NOT_FOUND, @@ -124,7 +92,7 @@ async def websocket_delete_registration(hass: HomeAssistantType, return error_message( msg['id'], ERR_UNAUTHORIZED, 'User is not registration owner') - del hass.data[DOMAIN][DATA_REGISTRATIONS][webhook_id] + await hass.config_entries.async_remove(config_entry.entry_id) hass.data[DOMAIN][DATA_DELETED_IDS].append(webhook_id) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 1036c02fd0de79..e00d7204a793bc 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -161,6 +161,7 @@ async def async_step_discovery(info): 'locative', 'luftdaten', 'mailgun', + 'mobile_app', 'mqtt', 'nest', 'openuv', diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 57265cf696dde5..acd0befda4e166 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -170,11 +170,13 @@ def async_create_entry(self, *, title: str, data: Dict, } @callback - def async_abort(self, *, reason: str) -> Dict: + def async_abort(self, *, reason: str, + description_placeholders: Optional[Dict] = None) -> Dict: """Abort the config flow.""" return { 'type': RESULT_TYPE_ABORT, 'flow_id': self.flow_id, 'handler': self.handler, - 'reason': reason + 'reason': reason, + 'description_placeholders': description_placeholders, } diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 87ed83d9a7ef3f..d5e4331f7b9b98 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -226,6 +226,7 @@ def async_step_user(self, user_input=None): data = yield from resp.json() data.pop('flow_id') assert data == { + 'description_placeholders': None, 'handler': 'test', 'reason': 'bla', 'type': 'abort' diff --git a/tests/components/mobile_app/__init__.py b/tests/components/mobile_app/__init__.py index 1f91eb7e442221..bed275a534d6a6 100644 --- a/tests/components/mobile_app/__init__.py +++ b/tests/components/mobile_app/__init__.py @@ -2,48 +2,59 @@ # pylint: disable=redefined-outer-name,unused-import import pytest +from tests.common import mock_device_registry + from homeassistant.setup import async_setup_component -from homeassistant.components.mobile_app.const import (DATA_DELETED_IDS, - DATA_REGISTRATIONS, - CONF_SECRET, - CONF_USER_ID, DOMAIN, +from homeassistant.components.mobile_app.const import (DATA_CONFIG_ENTRIES, + DATA_DELETED_IDS, + DATA_DEVICES, + DOMAIN, STORAGE_KEY, STORAGE_VERSION) -from homeassistant.const import CONF_WEBHOOK_ID + +from .const import REGISTER, REGISTER_CLEARTEXT @pytest.fixture -def webhook_client(hass, aiohttp_client, hass_storage, hass_admin_user): +def registry(hass): + """Return a configured device registry.""" + return mock_device_registry(hass) + + +@pytest.fixture +async def create_registrations(authed_api_client): + """Return two new registrations.""" + enc_reg = await authed_api_client.post( + '/api/mobile_app/registrations', json=REGISTER + ) + + assert enc_reg.status == 201 + enc_reg_json = await enc_reg.json() + + clear_reg = await authed_api_client.post( + '/api/mobile_app/registrations', json=REGISTER_CLEARTEXT + ) + + assert clear_reg.status == 201 + clear_reg_json = await clear_reg.json() + + return (enc_reg_json, clear_reg_json) + + +@pytest.fixture +async def webhook_client(hass, aiohttp_client, hass_storage, hass_admin_user): """mobile_app mock client.""" hass_storage[STORAGE_KEY] = { 'version': STORAGE_VERSION, 'data': { - DATA_REGISTRATIONS: { - 'mobile_app_test': { - CONF_SECRET: '58eb127991594dad934d1584bdee5f27', - 'supports_encryption': True, - CONF_WEBHOOK_ID: 'mobile_app_test', - 'device_name': 'Test Device', - CONF_USER_ID: hass_admin_user.id, - }, - 'mobile_app_test_cleartext': { - 'supports_encryption': False, - CONF_WEBHOOK_ID: 'mobile_app_test_cleartext', - 'device_name': 'Test Device (Cleartext)', - CONF_USER_ID: hass_admin_user.id, - } - }, - DATA_DELETED_IDS: [], + DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, } } - assert hass.loop.run_until_complete(async_setup_component( - hass, DOMAIN, { - DOMAIN: {} - })) + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) - return hass.loop.run_until_complete(aiohttp_client(hass.http.app)) + return await aiohttp_client(hass.http.app) @pytest.fixture diff --git a/tests/components/mobile_app/test_http_api.py b/tests/components/mobile_app/test_http_api.py index 7861e63459a32b..eb9d1f54d93c95 100644 --- a/tests/components/mobile_app/test_http_api.py +++ b/tests/components/mobile_app/test_http_api.py @@ -2,14 +2,15 @@ # pylint: disable=redefined-outer-name,unused-import import pytest -from homeassistant.components.mobile_app.const import CONF_SECRET +from homeassistant.components.mobile_app.const import CONF_SECRET, DOMAIN from homeassistant.const import CONF_WEBHOOK_ID +from homeassistant.setup import async_setup_component from .const import REGISTER, RENDER_TEMPLATE from . import authed_api_client # noqa: F401 -async def test_registration(hass_client, authed_api_client): # noqa: F811 +async def test_registration(hass, hass_client): # noqa: F811 """Test that registrations happen.""" try: # pylint: disable=unused-import @@ -21,7 +22,11 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811 import json - resp = await authed_api_client.post( + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + api_client = await hass_client() + + resp = await api_client.post( '/api/mobile_app/registrations', json=REGISTER ) @@ -30,6 +35,20 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811 assert CONF_WEBHOOK_ID in register_json assert CONF_SECRET in register_json + entries = hass.config_entries.async_entries(DOMAIN) + + assert entries[0].data['app_data'] == REGISTER['app_data'] + assert entries[0].data['app_id'] == REGISTER['app_id'] + assert entries[0].data['app_name'] == REGISTER['app_name'] + assert entries[0].data['app_version'] == REGISTER['app_version'] + assert entries[0].data['device_name'] == REGISTER['device_name'] + assert entries[0].data['manufacturer'] == REGISTER['manufacturer'] + assert entries[0].data['model'] == REGISTER['model'] + assert entries[0].data['os_name'] == REGISTER['os_name'] + assert entries[0].data['os_version'] == REGISTER['os_version'] + assert entries[0].data['supports_encryption'] == \ + REGISTER['supports_encryption'] + keylen = SecretBox.KEY_SIZE key = register_json[CONF_SECRET].encode("utf-8") key = key[:keylen] @@ -46,9 +65,7 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811 'encrypted_data': data, } - webhook_client = await hass_client() - - resp = await webhook_client.post( + resp = await api_client.post( '/api/webhook/{}'.format(register_json[CONF_WEBHOOK_ID]), json=container ) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index 75e8903c494524..a70e8ba1275290 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -1,5 +1,6 @@ """Webhook tests for mobile_app.""" # pylint: disable=redefined-outer-name,unused-import +import logging import pytest from homeassistant.components.mobile_app.const import CONF_SECRET @@ -8,16 +9,20 @@ from tests.common import async_mock_service -from . import authed_api_client, webhook_client # noqa: F401 +from . import (authed_api_client, create_registrations, # noqa: F401 + webhook_client) # noqa: F401 from .const import (CALL_SERVICE, FIRE_EVENT, REGISTER_CLEARTEXT, RENDER_TEMPLATE, UPDATE) +_LOGGER = logging.getLogger(__name__) -async def test_webhook_handle_render_template(webhook_client): # noqa: F811 + +async def test_webhook_handle_render_template(create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 """Test that we render templates properly.""" resp = await webhook_client.post( - '/api/webhook/mobile_app_test_cleartext', + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), json=RENDER_TEMPLATE ) @@ -27,12 +32,13 @@ async def test_webhook_handle_render_template(webhook_client): # noqa: F811 assert json == {'one': 'Hello world'} -async def test_webhook_handle_call_services(hass, webhook_client): # noqa: E501 F811 +async def test_webhook_handle_call_services(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: E501 F811 """Test that we call services properly.""" calls = async_mock_service(hass, 'test', 'mobile_app') resp = await webhook_client.post( - '/api/webhook/mobile_app_test_cleartext', + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), json=CALL_SERVICE ) @@ -41,7 +47,8 @@ async def test_webhook_handle_call_services(hass, webhook_client): # noqa: E501 assert len(calls) == 1 -async def test_webhook_handle_fire_event(hass, webhook_client): # noqa: F811 +async def test_webhook_handle_fire_event(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 """Test that we can fire events.""" events = [] @@ -53,7 +60,7 @@ def store_event(event): hass.bus.async_listen('test_event', store_event) resp = await webhook_client.post( - '/api/webhook/mobile_app_test_cleartext', + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), json=FIRE_EVENT ) @@ -93,10 +100,12 @@ async def test_webhook_update_registration(webhook_client, hass_client): # noqa assert CONF_SECRET not in update_json -async def test_webhook_returns_error_incorrect_json(webhook_client, caplog): # noqa: E501 F811 +async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 + create_registrations, # noqa: F401, F811, E501 + caplog): # noqa: E501 F811 """Test that an error is returned when JSON is invalid.""" resp = await webhook_client.post( - '/api/webhook/mobile_app_test_cleartext', + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), data='not json' ) @@ -106,7 +115,8 @@ async def test_webhook_returns_error_incorrect_json(webhook_client, caplog): # assert 'invalid JSON' in caplog.text -async def test_webhook_handle_decryption(webhook_client): # noqa: F811 +async def test_webhook_handle_decryption(webhook_client, # noqa: F811 + create_registrations): # noqa: F401, F811, E501 """Test that we can encrypt/decrypt properly.""" try: # pylint: disable=unused-import @@ -119,7 +129,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 import json keylen = SecretBox.KEY_SIZE - key = "58eb127991594dad934d1584bdee5f27".encode("utf-8") + key = create_registrations[0]['secret'].encode("utf-8") key = key[:keylen] key = key.ljust(keylen, b'\0') @@ -135,7 +145,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 } resp = await webhook_client.post( - '/api/webhook/mobile_app_test', + '/api/webhook/{}'.format(create_registrations[0]['webhook_id']), json=container ) @@ -151,10 +161,11 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811 assert json.loads(decrypted_data) == {'one': 'Hello world'} -async def test_webhook_requires_encryption(webhook_client): # noqa: F811 +async def test_webhook_requires_encryption(webhook_client, # noqa: F811 + create_registrations): # noqa: F401, F811, E501 """Test that encrypted registrations only accept encrypted data.""" resp = await webhook_client.post( - '/api/webhook/mobile_app_test', + '/api/webhook/{}'.format(create_registrations[0]['webhook_id']), json=RENDER_TEMPLATE ) diff --git a/tests/components/mobile_app/test_websocket_api.py b/tests/components/mobile_app/test_websocket_api.py index 614fd33974b165..ee656159d2e784 100644 --- a/tests/components/mobile_app/test_websocket_api.py +++ b/tests/components/mobile_app/test_websocket_api.py @@ -9,33 +9,6 @@ from .const import (CALL_SERVICE, REGISTER) -async def test_webocket_get_registration(hass, setup_ws, authed_api_client, # noqa: E501 F811 - hass_ws_client): - """Test get_registration websocket command.""" - register_resp = await authed_api_client.post( - '/api/mobile_app/registrations', json=REGISTER - ) - - assert register_resp.status == 201 - register_json = await register_resp.json() - assert CONF_WEBHOOK_ID in register_json - assert CONF_SECRET in register_json - - client = await hass_ws_client(hass) - await client.send_json({ - 'id': 5, - 'type': 'mobile_app/get_registration', - CONF_WEBHOOK_ID: register_json[CONF_WEBHOOK_ID], - }) - - msg = await client.receive_json() - - assert msg['id'] == 5 - assert msg['type'] == TYPE_RESULT - assert msg['success'] - assert msg['result']['app_id'] == 'io.homeassistant.mobile_app_test' - - async def test_webocket_get_user_registrations(hass, aiohttp_client, hass_ws_client, hass_read_only_access_token): From f0b7d76e269b4492dd957bd0a82c6d5893d2ac3a Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 17:24:53 -0700 Subject: [PATCH 014/605] Mobile App: Sensors (#21854) ## Description: **Related issue (if applicable):** fixes #21782 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- .../components/mobile_app/__init__.py | 36 +++-- .../components/mobile_app/binary_sensor.py | 54 +++++++ homeassistant/components/mobile_app/const.py | 56 ++++++- homeassistant/components/mobile_app/entity.py | 98 +++++++++++++ .../components/mobile_app/helpers.py | 5 +- homeassistant/components/mobile_app/sensor.py | 58 ++++++++ .../components/mobile_app/webhook.py | 107 ++++++++++++-- tests/components/mobile_app/__init__.py | 8 +- tests/components/mobile_app/test_entity.py | 137 ++++++++++++++++++ 9 files changed, 529 insertions(+), 30 deletions(-) create mode 100644 homeassistant/components/mobile_app/binary_sensor.py create mode 100644 homeassistant/components/mobile_app/entity.py create mode 100644 homeassistant/components/mobile_app/sensor.py create mode 100644 tests/components/mobile_app/test_entity.py diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index 1c348ea078265e..ecbe8d708476e7 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -3,13 +3,13 @@ from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.components.webhook import async_register as webhook_register from homeassistant.helpers import device_registry as dr -from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, ATTR_DEVICE_NAME, - ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, +from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, + ATTR_MODEL, ATTR_OS_VERSION, DATA_BINARY_SENSOR, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_DEVICES, - DATA_STORE, DOMAIN, STORAGE_KEY, STORAGE_VERSION) + DATA_SENSOR, DATA_STORE, DOMAIN, STORAGE_KEY, + STORAGE_VERSION) from .http_api import RegistrationsView from .webhook import handle_webhook @@ -22,19 +22,25 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the mobile app component.""" - hass.data[DOMAIN] = { - DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, - } - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) app_config = await store.async_load() if app_config is None: app_config = { - DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, + DATA_BINARY_SENSOR: {}, + DATA_CONFIG_ENTRIES: {}, + DATA_DELETED_IDS: [], + DATA_DEVICES: {}, + DATA_SENSOR: {} } - hass.data[DOMAIN] = app_config - hass.data[DOMAIN][DATA_STORE] = store + hass.data[DOMAIN] = { + DATA_BINARY_SENSOR: app_config.get(DATA_BINARY_SENSOR, {}), + DATA_CONFIG_ENTRIES: {}, + DATA_DELETED_IDS: app_config.get(DATA_DELETED_IDS, []), + DATA_DEVICES: {}, + DATA_SENSOR: app_config.get(DATA_SENSOR, {}), + DATA_STORE: store, + } hass.http.register_view(RegistrationsView()) register_websocket_handlers(hass) @@ -79,9 +85,11 @@ async def async_setup_entry(hass, entry): webhook_register(hass, DOMAIN, registration_name, webhook_id, handle_webhook) - if ATTR_APP_COMPONENT in registration: - load_platform(hass, registration[ATTR_APP_COMPONENT], DOMAIN, {}, - {DOMAIN: {}}) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, + DATA_BINARY_SENSOR)) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, DATA_SENSOR)) return True diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py new file mode 100644 index 00000000000000..289a50584c92d3 --- /dev/null +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -0,0 +1,54 @@ +"""Binary sensor platform for mobile_app.""" +from functools import partial + +from homeassistant.const import CONF_WEBHOOK_ID +from homeassistant.core import callback +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import (ATTR_SENSOR_STATE, + ATTR_SENSOR_TYPE_BINARY_SENSOR as ENTITY_TYPE, + DATA_DEVICES, DOMAIN) + +from .entity import MobileAppEntity + +DEPENDENCIES = ['mobile_app'] + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up mobile app binary sensor from a config entry.""" + entities = list() + + webhook_id = config_entry.data[CONF_WEBHOOK_ID] + + for config in hass.data[DOMAIN][ENTITY_TYPE].values(): + if config[CONF_WEBHOOK_ID] != webhook_id: + continue + + device = hass.data[DOMAIN][DATA_DEVICES][webhook_id] + + entities.append(MobileAppBinarySensor(config, device, config_entry)) + + async_add_entities(entities) + + @callback + def handle_sensor_registration(webhook_id, data): + if data[CONF_WEBHOOK_ID] != webhook_id: + return + + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] + + async_add_entities([MobileAppBinarySensor(data, device, config_entry)]) + + async_dispatcher_connect(hass, + '{}_{}_register'.format(DOMAIN, ENTITY_TYPE), + partial(handle_sensor_registration, webhook_id)) + + +class MobileAppBinarySensor(MobileAppEntity, BinarySensorDevice): + """Representation of an mobile app binary sensor.""" + + @property + def is_on(self): + """Return the state of the binary sensor.""" + return self._config[ATTR_SENSOR_STATE] diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 3ba029fec0e96a..d38df31b214d42 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -1,6 +1,9 @@ """Constants for mobile_app.""" import voluptuous as vol +from homeassistant.components.binary_sensor import (DEVICE_CLASSES as + BINARY_SENSOR_CLASSES) +from homeassistant.components.sensor import DEVICE_CLASSES as SENSOR_CLASSES from homeassistant.components.device_tracker import (ATTR_BATTERY, ATTR_GPS, ATTR_GPS_ACCURACY, @@ -17,9 +20,11 @@ CONF_SECRET = 'secret' CONF_USER_ID = 'user_id' +DATA_BINARY_SENSOR = 'binary_sensor' DATA_CONFIG_ENTRIES = 'config_entries' DATA_DELETED_IDS = 'deleted_ids' DATA_DEVICES = 'devices' +DATA_SENSOR = 'sensor' DATA_STORE = 'store' ATTR_APP_COMPONENT = 'app_component' @@ -54,16 +59,22 @@ ERR_ENCRYPTION_REQUIRED = 'encryption_required' ERR_INVALID_COMPONENT = 'invalid_component' +ERR_SENSOR_NOT_REGISTERED = 'not_registered' +ERR_SENSOR_DUPLICATE_UNIQUE_ID = 'duplicate_unique_id' WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location' WEBHOOK_TYPE_UPDATE_REGISTRATION = 'update_registration' +WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, - WEBHOOK_TYPE_UPDATE_REGISTRATION] + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_UPDATE_REGISTRATION, + WEBHOOK_TYPE_UPDATE_SENSOR_STATES] REGISTRATION_SCHEMA = vol.Schema({ @@ -91,7 +102,7 @@ WEBHOOK_PAYLOAD_SCHEMA = vol.Schema({ vol.Required(ATTR_WEBHOOK_TYPE): cv.string, # vol.In(WEBHOOK_TYPES) - vol.Required(ATTR_WEBHOOK_DATA, default={}): dict, + vol.Required(ATTR_WEBHOOK_DATA, default={}): vol.Any(dict, list), vol.Optional(ATTR_WEBHOOK_ENCRYPTED, default=False): cv.boolean, vol.Optional(ATTR_WEBHOOK_ENCRYPTED_DATA): cv.string, }) @@ -125,10 +136,49 @@ vol.Optional(ATTR_VERTICAL_ACCURACY): cv.positive_int, }) +ATTR_SENSOR_ATTRIBUTES = 'attributes' +ATTR_SENSOR_DEVICE_CLASS = 'device_class' +ATTR_SENSOR_ICON = 'icon' +ATTR_SENSOR_NAME = 'name' +ATTR_SENSOR_STATE = 'state' +ATTR_SENSOR_TYPE = 'type' +ATTR_SENSOR_TYPE_BINARY_SENSOR = 'binary_sensor' +ATTR_SENSOR_TYPE_SENSOR = 'sensor' +ATTR_SENSOR_UNIQUE_ID = 'unique_id' +ATTR_SENSOR_UOM = 'unit_of_measurement' + +SENSOR_TYPES = [ATTR_SENSOR_TYPE_BINARY_SENSOR, ATTR_SENSOR_TYPE_SENSOR] + +COMBINED_CLASSES = sorted(set(BINARY_SENSOR_CLASSES + SENSOR_CLASSES)) + +SIGNAL_SENSOR_UPDATE = DOMAIN + '_sensor_update' + +REGISTER_SENSOR_SCHEMA = vol.Schema({ + vol.Optional(ATTR_SENSOR_ATTRIBUTES, default={}): dict, + vol.Optional(ATTR_SENSOR_DEVICE_CLASS): vol.All(vol.Lower, + vol.In(COMBINED_CLASSES)), + vol.Required(ATTR_SENSOR_NAME): cv.string, + vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), + vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, + vol.Required(ATTR_SENSOR_UOM): cv.string, + vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), + vol.Optional(ATTR_SENSOR_ICON, default='mdi:cellphone'): cv.icon, +}) + +UPDATE_SENSOR_STATE_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({ + vol.Optional(ATTR_SENSOR_ATTRIBUTES, default={}): dict, + vol.Optional(ATTR_SENSOR_ICON, default='mdi:cellphone'): cv.icon, + vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), + vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), + vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, +})]) + WEBHOOK_SCHEMAS = { WEBHOOK_TYPE_CALL_SERVICE: CALL_SERVICE_SCHEMA, WEBHOOK_TYPE_FIRE_EVENT: FIRE_EVENT_SCHEMA, + WEBHOOK_TYPE_REGISTER_SENSOR: REGISTER_SENSOR_SCHEMA, WEBHOOK_TYPE_RENDER_TEMPLATE: RENDER_TEMPLATE_SCHEMA, WEBHOOK_TYPE_UPDATE_LOCATION: UPDATE_LOCATION_SCHEMA, WEBHOOK_TYPE_UPDATE_REGISTRATION: UPDATE_REGISTRATION_SCHEMA, + WEBHOOK_TYPE_UPDATE_SENSOR_STATES: UPDATE_SENSOR_STATE_SCHEMA, } diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py new file mode 100644 index 00000000000000..05736b3a689b37 --- /dev/null +++ b/homeassistant/components/mobile_app/entity.py @@ -0,0 +1,98 @@ +"""A entity class for mobile_app.""" +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_WEBHOOK_ID +from homeassistant.core import callback +from homeassistant.helpers.device_registry import DeviceEntry +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import Entity + +from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, + ATTR_MODEL, ATTR_OS_VERSION, ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ICON, + ATTR_SENSOR_NAME, ATTR_SENSOR_TYPE, ATTR_SENSOR_UNIQUE_ID, + DOMAIN, SIGNAL_SENSOR_UPDATE) + + +class MobileAppEntity(Entity): + """Representation of an mobile app entity.""" + + def __init__(self, config: dict, device: DeviceEntry, entry: ConfigEntry): + """Initialize the sensor.""" + self._config = config + self._device = device + self._entry = entry + self._registration = entry.data + self._sensor_id = "{}_{}".format(self._registration[CONF_WEBHOOK_ID], + config[ATTR_SENSOR_UNIQUE_ID]) + self._entity_type = config[ATTR_SENSOR_TYPE] + self.unsub_dispatcher = None + + async def async_added_to_hass(self): + """Register callbacks.""" + self.unsub_dispatcher = async_dispatcher_connect(self.hass, + SIGNAL_SENSOR_UPDATE, + self._handle_update) + + async def async_will_remove_from_hass(self): + """Disconnect dispatcher listener when removed.""" + if self.unsub_dispatcher is not None: + self.unsub_dispatcher() + + @property + def should_poll(self) -> bool: + """Declare that this entity pushes its state to HA.""" + return False + + @property + def name(self): + """Return the name of the mobile app sensor.""" + return self._config[ATTR_SENSOR_NAME] + + @property + def device_class(self): + """Return the device class.""" + return self._config.get(ATTR_SENSOR_DEVICE_CLASS) + + @property + def device_state_attributes(self): + """Return the device state attributes.""" + return self._config[ATTR_SENSOR_ATTRIBUTES] + + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return self._config[ATTR_SENSOR_ICON] + + @property + def unique_id(self): + """Return the unique ID of this sensor.""" + return self._sensor_id + + @property + def device_info(self): + """Return device registry information for this entity.""" + return { + 'identifiers': { + (ATTR_DEVICE_ID, self._registration[ATTR_DEVICE_ID]), + (CONF_WEBHOOK_ID, self._registration[CONF_WEBHOOK_ID]) + }, + 'manufacturer': self._registration[ATTR_MANUFACTURER], + 'model': self._registration[ATTR_MODEL], + 'device_name': self._registration[ATTR_DEVICE_NAME], + 'sw_version': self._registration[ATTR_OS_VERSION], + 'config_entries': self._device.config_entries + } + + async def async_update(self): + """Get the latest state of the sensor.""" + data = self.hass.data[DOMAIN] + try: + self._config = data[self._entity_type][self._sensor_id] + except KeyError: + return + + @callback + def _handle_update(self, data): + """Handle async event updates.""" + self._config = data + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index 5ec3b99b2917e5..60bd8b4e1d6bec 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -11,7 +11,8 @@ from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME, ATTR_APP_VERSION, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, ATTR_SUPPORTS_ENCRYPTION, - CONF_SECRET, CONF_USER_ID, DATA_DELETED_IDS, DOMAIN) + CONF_SECRET, CONF_USER_ID, DATA_BINARY_SENSOR, + DATA_DELETED_IDS, DATA_SENSOR, DOMAIN) _LOGGER = logging.getLogger(__name__) @@ -123,7 +124,9 @@ def safe_registration(registration: Dict) -> Dict: def savable_state(hass: HomeAssistantType) -> Dict: """Return a clean object containing things that should be saved.""" return { + DATA_BINARY_SENSOR: hass.data[DOMAIN][DATA_BINARY_SENSOR], DATA_DELETED_IDS: hass.data[DOMAIN][DATA_DELETED_IDS], + DATA_SENSOR: hass.data[DOMAIN][DATA_SENSOR], } diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py new file mode 100644 index 00000000000000..c6a53ce57ec7c9 --- /dev/null +++ b/homeassistant/components/mobile_app/sensor.py @@ -0,0 +1,58 @@ +"""Sensor platform for mobile_app.""" +from functools import partial + +from homeassistant.const import CONF_WEBHOOK_ID +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import (ATTR_SENSOR_STATE, + ATTR_SENSOR_TYPE_SENSOR as ENTITY_TYPE, + ATTR_SENSOR_UOM, DATA_DEVICES, DOMAIN) + +from .entity import MobileAppEntity + +DEPENDENCIES = ['mobile_app'] + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up mobile app sensor from a config entry.""" + entities = list() + + webhook_id = config_entry.data[CONF_WEBHOOK_ID] + + for config in hass.data[DOMAIN][ENTITY_TYPE].values(): + if config[CONF_WEBHOOK_ID] != webhook_id: + continue + + device = hass.data[DOMAIN][DATA_DEVICES][webhook_id] + + entities.append(MobileAppSensor(config, device, config_entry)) + + async_add_entities(entities) + + @callback + def handle_sensor_registration(webhook_id, data): + if data[CONF_WEBHOOK_ID] != webhook_id: + return + + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] + + async_add_entities([MobileAppSensor(data, device, config_entry)]) + + async_dispatcher_connect(hass, + '{}_{}_register'.format(DOMAIN, ENTITY_TYPE), + partial(handle_sensor_registration, webhook_id)) + + +class MobileAppSensor(MobileAppEntity): + """Representation of an mobile app sensor.""" + + @property + def state(self): + """Return the state of the sensor.""" + return self._config[ATTR_SENSOR_STATE] + + @property + def unit_of_measurement(self): + """Return the unit of measurement this sensor expresses itself in.""" + return self._config[ATTR_SENSOR_UOM] diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 1fab29160b7d24..aafa6046d110a5 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -10,30 +10,38 @@ SERVICE_SEE as DT_SEE) from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, - CONF_WEBHOOK_ID, HTTP_BAD_REQUEST) + CONF_WEBHOOK_ID, HTTP_BAD_REQUEST, + HTTP_CREATED) from homeassistant.core import EventOrigin -from homeassistant.exceptions import (ServiceNotFound, TemplateError) +from homeassistant.exceptions import (HomeAssistantError, + ServiceNotFound, TemplateError) from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.template import attach from homeassistant.helpers.typing import HomeAssistantType from .const import (ATTR_ALTITUDE, ATTR_BATTERY, ATTR_COURSE, ATTR_DEVICE_ID, ATTR_DEVICE_NAME, ATTR_EVENT_DATA, ATTR_EVENT_TYPE, ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_LOCATION_NAME, - ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, ATTR_SPEED, + ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, + ATTR_SENSOR_TYPE, ATTR_SENSOR_UNIQUE_ID, ATTR_SPEED, ATTR_SUPPORTS_ENCRYPTION, ATTR_TEMPLATE, ATTR_TEMPLATE_VARIABLES, ATTR_VERTICAL_ACCURACY, ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, - CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DOMAIN, - ERR_ENCRYPTION_REQUIRED, WEBHOOK_PAYLOAD_SCHEMA, + CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, + DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, + ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, + SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, - WEBHOOK_TYPE_UPDATE_REGISTRATION) + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_UPDATE_REGISTRATION, + WEBHOOK_TYPE_UPDATE_SENSOR_STATES) + from .helpers import (_decrypt_payload, empty_okay_response, error_response, - registration_context, safe_registration, + registration_context, safe_registration, savable_state, webhook_response) @@ -79,6 +87,10 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA] webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_data) + if webhook_type not in WEBHOOK_SCHEMAS: + _LOGGER.error('Received invalid webhook type: %s', webhook_type) + return empty_okay_response() + try: data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) except vol.Invalid as ex: @@ -172,3 +184,80 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, return webhook_response(safe_registration(new_registration), registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_REGISTER_SENSOR: + entity_type = data[ATTR_SENSOR_TYPE] + + unique_id = data[ATTR_SENSOR_UNIQUE_ID] + + unique_store_key = "{}_{}".format(webhook_id, unique_id) + + if unique_store_key in hass.data[DOMAIN][entity_type]: + _LOGGER.error("Refusing to re-register existing sensor %s!", + unique_id) + return error_response(ERR_SENSOR_DUPLICATE_UNIQUE_ID, + "{} {} already exists!".format(entity_type, + unique_id), + status=409) + + data[CONF_WEBHOOK_ID] = webhook_id + + hass.data[DOMAIN][entity_type][unique_store_key] = data + + try: + await hass.data[DOMAIN][DATA_STORE].async_save(savable_state(hass)) + except HomeAssistantError as ex: + _LOGGER.error("Error registering sensor: %s", ex) + return empty_okay_response() + + register_signal = '{}_{}_register'.format(DOMAIN, + data[ATTR_SENSOR_TYPE]) + async_dispatcher_send(hass, register_signal, data) + + return webhook_response({"status": "registered"}, + registration=registration, status=HTTP_CREATED, + headers=headers) + + if webhook_type == WEBHOOK_TYPE_UPDATE_SENSOR_STATES: + resp = {} + for sensor in data: + entity_type = sensor[ATTR_SENSOR_TYPE] + + unique_id = sensor[ATTR_SENSOR_UNIQUE_ID] + + unique_store_key = "{}_{}".format(webhook_id, unique_id) + + if unique_store_key not in hass.data[DOMAIN][entity_type]: + _LOGGER.error("Refusing to update non-registered sensor: %s", + unique_store_key) + err_msg = '{} {} is not registered'.format(entity_type, + unique_id) + resp[unique_id] = { + 'success': False, + 'error': { + 'code': ERR_SENSOR_NOT_REGISTERED, + 'message': err_msg + } + } + continue + + entry = hass.data[DOMAIN][entity_type][unique_store_key] + + new_state = {**entry, **sensor} + + hass.data[DOMAIN][entity_type][unique_store_key] = new_state + + safe = savable_state(hass) + + try: + await hass.data[DOMAIN][DATA_STORE].async_save(safe) + except HomeAssistantError as ex: + _LOGGER.error("Error updating mobile_app registration: %s", ex) + return empty_okay_response() + + async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, new_state) + + resp[unique_id] = {"status": "okay"} + + return webhook_response(resp, registration=registration, + headers=headers) diff --git a/tests/components/mobile_app/__init__.py b/tests/components/mobile_app/__init__.py index bed275a534d6a6..cf617ff05289f7 100644 --- a/tests/components/mobile_app/__init__.py +++ b/tests/components/mobile_app/__init__.py @@ -6,9 +6,9 @@ from homeassistant.setup import async_setup_component -from homeassistant.components.mobile_app.const import (DATA_CONFIG_ENTRIES, +from homeassistant.components.mobile_app.const import (DATA_BINARY_SENSOR, DATA_DELETED_IDS, - DATA_DEVICES, + DATA_SENSOR, DOMAIN, STORAGE_KEY, STORAGE_VERSION) @@ -48,7 +48,9 @@ async def webhook_client(hass, aiohttp_client, hass_storage, hass_admin_user): hass_storage[STORAGE_KEY] = { 'version': STORAGE_VERSION, 'data': { - DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], DATA_DEVICES: {}, + DATA_BINARY_SENSOR: {}, + DATA_DELETED_IDS: [], + DATA_SENSOR: {} } } diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py new file mode 100644 index 00000000000000..d8cb91a8bc61ca --- /dev/null +++ b/tests/components/mobile_app/test_entity.py @@ -0,0 +1,137 @@ +"""Entity tests for mobile_app.""" +# pylint: disable=redefined-outer-name,unused-import +import logging + +from . import (authed_api_client, create_registrations, # noqa: F401 + webhook_client) # noqa: F401 + +_LOGGER = logging.getLogger(__name__) + + +async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401, F811, E501 + """Test that sensors can be registered and updated.""" + webhook_id = create_registrations[1]['webhook_id'] + webhook_url = '/api/webhook/{}'.format(webhook_id) + + reg_resp = await webhook_client.post( + webhook_url, + json={ + 'type': 'register_sensor', + 'data': { + 'attributes': { + 'foo': 'bar' + }, + 'device_class': 'battery', + 'icon': 'mdi:battery', + 'name': 'Battery State', + 'state': 100, + 'type': 'sensor', + 'unique_id': 'battery_state', + 'unit_of_measurement': '%' + } + } + ) + + assert reg_resp.status == 201 + + json = await reg_resp.json() + assert json == {'status': 'registered'} + + # 3 because we require device_tracker which adds zone.home and + # group.all_devices + assert len(hass.states.async_all()) == 3 + + entity = hass.states.async_all()[2] + + assert entity.attributes['device_class'] == 'battery' + assert entity.attributes['icon'] == 'mdi:battery' + assert entity.attributes['unit_of_measurement'] == '%' + assert entity.attributes['foo'] == 'bar' + assert entity.domain == 'sensor' + assert entity.name == 'Battery State' + assert entity.state == '100' + + update_resp = await webhook_client.post( + webhook_url, + json={ + 'type': 'update_sensor_states', + 'data': [ + { + 'icon': 'mdi:battery-unknown', + 'state': 123, + 'type': 'sensor', + 'unique_id': 'battery_state' + } + ] + } + ) + + assert update_resp.status == 200 + + updated_entity = hass.states.async_all()[2] + + assert updated_entity.state == '123' + + +async def test_sensor_must_register(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F401, F811, E501 + """Test that sensors must be registered before updating.""" + webhook_id = create_registrations[1]['webhook_id'] + webhook_url = '/api/webhook/{}'.format(webhook_id) + resp = await webhook_client.post( + webhook_url, + json={ + 'type': 'update_sensor_states', + 'data': [ + { + 'state': 123, + 'type': 'sensor', + 'unique_id': 'battery_state' + } + ] + } + ) + + assert resp.status == 200 + + json = await resp.json() + assert json['battery_state']['success'] is False + assert json['battery_state']['error']['code'] == 'not_registered' + + +async def test_sensor_id_no_dupes(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F401, F811, E501 + """Test that sensors must have a unique ID.""" + webhook_id = create_registrations[1]['webhook_id'] + webhook_url = '/api/webhook/{}'.format(webhook_id) + + payload = { + 'type': 'register_sensor', + 'data': { + 'attributes': { + 'foo': 'bar' + }, + 'device_class': 'battery', + 'icon': 'mdi:battery', + 'name': 'Battery State', + 'state': 100, + 'type': 'sensor', + 'unique_id': 'battery_state', + 'unit_of_measurement': '%' + } + } + + reg_resp = await webhook_client.post(webhook_url, json=payload) + + assert reg_resp.status == 201 + + reg_json = await reg_resp.json() + assert reg_json == {'status': 'registered'} + + dupe_resp = await webhook_client.post(webhook_url, json=payload) + + assert dupe_resp.status == 409 + + dupe_json = await dupe_resp.json() + assert dupe_json['success'] is False + assert dupe_json['error']['code'] == 'duplicate_unique_id' From 25a7f71ec27370cafd0abaf6fbc6e352622e60b3 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Thu, 14 Mar 2019 02:25:07 -0700 Subject: [PATCH 015/605] Bump androidtv to 0.0.11 (#22025) --- homeassistant/components/androidtv/media_player.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index ab43dc8c6ead17..458fdff87fd91b 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.10'] +REQUIREMENTS = ['androidtv==0.0.11'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 9200b803b8f391..c2a75a11d8093b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -158,7 +158,7 @@ alpha_vantage==2.1.0 amcrest==1.2.5 # homeassistant.components.androidtv.media_player -androidtv==0.0.10 +androidtv==0.0.11 # homeassistant.components.switch.anel_pwrctrl anel_pwrctrl-homeassistant==0.0.1.dev2 From 4835fb2c57c4937110000eb7e7dca4e7e577ed4d Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 21:55:30 -0700 Subject: [PATCH 016/605] Mobile App: Enable loading via discovery (surprise inside!) (#22027) ![](http://funpeep.com/wp-content/uploads/2014/04/Cute-White-Cat-Wallpaper.jpg) --- homeassistant/components/discovery/__init__.py | 4 +++- requirements_all.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 2f94cf48f4dc8a..6a561e570c5a70 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -21,7 +21,7 @@ from homeassistant.helpers.discovery import async_load_platform, async_discover import homeassistant.util.dt as dt_util -REQUIREMENTS = ['netdisco==2.4.0'] +REQUIREMENTS = ['netdisco==2.5.0'] DOMAIN = 'discovery' @@ -40,6 +40,7 @@ SERVICE_IGD = 'igd' SERVICE_IKEA_TRADFRI = 'ikea_tradfri' SERVICE_KONNECTED = 'konnected' +SERVICE_MOBILE_APP = 'hass_mobile_app' SERVICE_NETGEAR = 'netgear_router' SERVICE_OCTOPRINT = 'octoprint' SERVICE_ROKU = 'roku' @@ -63,6 +64,7 @@ } SERVICE_HANDLERS = { + SERVICE_MOBILE_APP: ('mobile_app', None), SERVICE_HASS_IOS_APP: ('ios', None), SERVICE_NETGEAR: ('device_tracker', None), SERVICE_WEMO: ('wemo', None), diff --git a/requirements_all.txt b/requirements_all.txt index c2a75a11d8093b..4f8d598665bb1a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -741,7 +741,7 @@ nessclient==0.9.14 netdata==0.1.2 # homeassistant.components.discovery -netdisco==2.4.0 +netdisco==2.5.0 # homeassistant.components.sensor.neurio_energy neurio==0.3.1 From 11ebb3f24eea916b2a427f4542d37a396f5ed055 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 13 Mar 2019 22:05:56 -0700 Subject: [PATCH 017/605] Mobile App: Discovery to default configuration.yaml, zeroconf to default_config (#22028) * Move discovery into default configuration.yaml * Add zeroconf to default_config --- homeassistant/components/default_config/__init__.py | 2 +- homeassistant/config.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/default_config/__init__.py b/homeassistant/components/default_config/__init__.py index badc403c7c8359..888a4d51c956f7 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -6,7 +6,6 @@ 'cloud', 'config', 'conversation', - 'discovery', 'frontend', 'history', 'logbook', @@ -17,6 +16,7 @@ 'sun', 'system_health', 'updater', + 'zeroconf', ) diff --git a/homeassistant/config.py b/homeassistant/config.py index db59e2c2744fe1..19b8087e538dfd 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -75,6 +75,9 @@ # http: # base_url: example.duckdns.org:8123 +# Discover some devices automatically +discovery: + # Sensors sensor: # Weather prediction From 0029dc3813b42abdc5b7c642babeced1ad5a9a77 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 14 Mar 2019 19:46:59 -0700 Subject: [PATCH 018/605] Mobile App: Expose Cloud Remote UI FQDN in registration response (#22055) * Add a callback to get the cloud remote UI FQDN * Expose Cloud Remote UI FQDN in the registration response * Return a URL instead of FQDN --- homeassistant/components/cloud/__init__.py | 10 ++++++++++ homeassistant/components/mobile_app/const.py | 1 + homeassistant/components/mobile_app/http_api.py | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 2e324f0673802c..3e3d6f975e9a30 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -118,6 +118,16 @@ async def async_delete_cloudhook(hass, webhook_id: str) -> None: await hass.data[DOMAIN].cloudhooks.async_delete(webhook_id) +@bind_hass +@callback +def async_remote_ui_url(hass) -> str: + """Get the remote UI URL.""" + if not async_is_logged_in(hass): + raise CloudNotAvailable + + return "https://" + hass.data[DOMAIN].remote.instance_domain + + def is_cloudhook_request(request): """Test if a request came from a cloudhook. diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index d38df31b214d42..3aa4626da29c41 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -17,6 +17,7 @@ STORAGE_VERSION = 1 CONF_CLOUDHOOK_URL = 'cloudhook_url' +CONF_REMOTE_UI_URL = 'remote_ui_url' CONF_SECRET = 'secret' CONF_USER_ID = 'user_id' diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index 8076d217cac47d..2ae8f441e52399 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -5,7 +5,9 @@ from aiohttp.web import Response, Request from homeassistant.auth.util import generate_secret -from homeassistant.components.cloud import async_create_cloudhook +from homeassistant.components.cloud import (async_create_cloudhook, + async_remote_ui_url, + CloudNotAvailable) from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.const import (HTTP_CREATED, CONF_WEBHOOK_ID) @@ -13,7 +15,8 @@ from homeassistant.loader import get_component from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, - ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, CONF_SECRET, + ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, + CONF_REMOTE_UI_URL, CONF_SECRET, CONF_USER_ID, DOMAIN, ERR_INVALID_COMPONENT, REGISTRATION_SCHEMA) @@ -67,8 +70,15 @@ async def post(self, request: Request, data: Dict) -> Response: hass.config_entries.flow.async_init(DOMAIN, context=ctx, data=data)) + remote_ui_url = None + try: + remote_ui_url = async_remote_ui_url(hass) + except CloudNotAvailable: + pass + return self.json({ CONF_CLOUDHOOK_URL: data.get(CONF_CLOUDHOOK_URL), + CONF_REMOTE_UI_URL: remote_ui_url, CONF_SECRET: data.get(CONF_SECRET), CONF_WEBHOOK_ID: data[CONF_WEBHOOK_ID], }, status_code=HTTP_CREATED) From 8f103454687869d2fd0e71e20fb84783efc4032a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 07:41:34 -0700 Subject: [PATCH 019/605] Return config entry ID after creation (#22060) --- .../components/config/config_entries.py | 22 ++++++++++++++++++- .../components/config/test_config_entries.py | 10 +++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/config/config_entries.py b/homeassistant/components/config/config_entries.py index 65f65cbcec5533..8865ff39ceaf6b 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -118,6 +118,16 @@ async def post(self, request): # pylint: disable=no-value-for-parameter return await super().post(request) + def _prepare_result_json(self, result): + """Convert result to JSON.""" + if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + return super()._prepare_result_json(result) + + data = result.copy() + data['result'] = data['result'].entry_id + data.pop('data') + return data + class ConfigManagerFlowResourceView(FlowManagerResourceView): """View to interact with the flow manager.""" @@ -143,6 +153,16 @@ async def post(self, request, flow_id): # pylint: disable=no-value-for-parameter return await super().post(request, flow_id) + def _prepare_result_json(self, result): + """Convert result to JSON.""" + if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + return super()._prepare_result_json(result) + + data = result.copy() + data['result'] = data['result'].entry_id + data.pop('data') + return data + class ConfigManagerAvailableFlowView(HomeAssistantView): """View to query available flows.""" @@ -175,7 +195,7 @@ async def post(self, request): return await super().post(request) -class OptionManagerFlowResourceView(ConfigManagerFlowResourceView): +class OptionManagerFlowResourceView(FlowManagerResourceView): """View to interact with the option flow manager.""" url = '/api/config/config_entries/options/flow/{flow_id}' diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index d5e4331f7b9b98..852a5adf6a2cc3 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -255,6 +255,10 @@ def async_step_user(self, user_input=None): json={'handler': 'test'}) assert resp.status == 200 + + entries = hass.config_entries.async_entries('test') + assert len(entries) == 1 + data = yield from resp.json() data.pop('flow_id') assert data == { @@ -262,6 +266,7 @@ def async_step_user(self, user_input=None): 'title': 'Test Entry', 'type': 'create_entry', 'version': 1, + 'result': entries[0].entry_id, 'description': None, 'description_placeholders': None, } @@ -317,6 +322,10 @@ def async_step_account(self, user_input=None): '/api/config/config_entries/flow/{}'.format(flow_id), json={'user_title': 'user-title'}) assert resp.status == 200 + + entries = hass.config_entries.async_entries('test') + assert len(entries) == 1 + data = yield from resp.json() data.pop('flow_id') assert data == { @@ -324,6 +333,7 @@ def async_step_account(self, user_input=None): 'type': 'create_entry', 'title': 'user-title', 'version': 1, + 'result': entries[0].entry_id, 'description': None, 'description_placeholders': None, } From 3ec8b5a170e5f0e85e6f81f92507ad76ce54f229 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 15 Mar 2019 10:01:15 -0700 Subject: [PATCH 020/605] Correct context (#22061) --- homeassistant/components/alexa/smart_home.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index a856a3d8e8252d..c87b2c3f624afa 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -1,21 +1,21 @@ """Support for alexa Smart Home Skill API.""" import asyncio -from collections import OrderedDict -from datetime import datetime import json import logging import math +from collections import OrderedDict +from datetime import datetime from uuid import uuid4 import aiohttp import async_timeout +import homeassistant.core as ha +import homeassistant.util.color as color_util from homeassistant.components import ( alert, automation, binary_sensor, cover, fan, group, http, input_boolean, light, lock, media_player, scene, script, sensor, switch) from homeassistant.components.climate import const as climate -from homeassistant.helpers import aiohttp_client -from homeassistant.helpers.event import async_track_state_change from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, CLOUD_NEVER_EXPOSED_ENTITIES, @@ -25,14 +25,14 @@ SERVICE_UNLOCK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_UP, SERVICE_VOLUME_SET, SERVICE_VOLUME_MUTE, STATE_LOCKED, STATE_ON, STATE_OFF, STATE_UNAVAILABLE, STATE_UNLOCKED, TEMP_CELSIUS, TEMP_FAHRENHEIT, MATCH_ALL) -import homeassistant.core as ha -import homeassistant.util.color as color_util +from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.event import async_track_state_change from homeassistant.util.decorator import Registry from homeassistant.util.temperature import convert as convert_temperature +from .auth import Auth from .const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_ENDPOINT, \ CONF_ENTITY_CONFIG, CONF_FILTER, DATE_FORMAT, DEFAULT_TIMEOUT -from .auth import Auth _LOGGER = logging.getLogger(__name__) @@ -1115,12 +1115,15 @@ async def post(self, request): the response. """ hass = request.app['hass'] + user = request[http.KEY_HASS_USER] message = await request.json() _LOGGER.debug("Received Alexa Smart Home request: %s", message) response = await async_handle_message( - hass, self.smart_home_config, message) + hass, self.smart_home_config, message, + context=ha.Context(user_id=user.id) + ) _LOGGER.debug("Sending Alexa Smart Home response: %s", response) return b'' if response is None else self.json(response) From ac1aeb35a6b950f5850f6ca1f2347431b1b79b74 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 15 Mar 2019 18:39:53 +0100 Subject: [PATCH 021/605] Binary Sensor for Remote UI & Fix timezone (#22076) * Binary Sensor for Remote UI * Fix lint * Revert make hass public * Add tests --- homeassistant/components/cloud/__init__.py | 4 +- .../components/cloud/binary_sensor.py | 73 +++++++++++++++++++ homeassistant/components/cloud/client.py | 15 +++- homeassistant/components/cloud/const.py | 2 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_binary_sensor.py | 32 ++++++++ 7 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/cloud/binary_sensor.py create mode 100644 tests/components/cloud/test_binary_sensor.py diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 3e3d6f975e9a30..9f6e678e41737b 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.5'] +REQUIREMENTS = ['hass-nabucasa==0.7'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) @@ -193,4 +193,6 @@ async def _service_handler(service): DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler) await http_api.async_setup(hass) + hass.async_create_task(hass.helpers.discovery.async_load_platform( + 'binary_sensor', DOMAIN, {}, config)) return True diff --git a/homeassistant/components/cloud/binary_sensor.py b/homeassistant/components/cloud/binary_sensor.py new file mode 100644 index 00000000000000..874c3420c5844e --- /dev/null +++ b/homeassistant/components/cloud/binary_sensor.py @@ -0,0 +1,73 @@ +"""Support for Home Assistant Cloud binary sensors.""" +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN + +DEPENDENCIES = ['cloud'] + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the cloud binary sensors.""" + if discovery_info is None: + return + cloud = hass.data[DOMAIN] + + async_add_entities([CloudRemoteBinary(cloud)]) + + +class CloudRemoteBinary(BinarySensorDevice): + """Representation of an Cloud Remote UI Connection binary sensor.""" + + def __init__(self, cloud): + """Initialize the binary sensor.""" + self.cloud = cloud + self._unsub_dispatcher = None + + @property + def name(self) -> str: + """Return the name of the binary sensor, if any.""" + return "Remote UI" + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return "cloud-remote-ui-connectivity" + + @property + def is_on(self) -> bool: + """Return true if the binary sensor is on.""" + return self.cloud.remote.is_connected + + @property + def device_class(self) -> str: + """Return the class of this device, from component DEVICE_CLASSES.""" + return 'connectivity' + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self.cloud.remote.certificate is not None + + @property + def should_poll(self) -> bool: + """Return True if entity has to be polled for state.""" + return False + + async def async_added_to_hass(self): + """Register update dispatcher.""" + @callback + def async_state_update(data): + """Update callback.""" + self.async_write_ha_state() + + self._unsub_dispatcher = async_dispatcher_connect( + self.hass, DISPATCHER_REMOTE_UPDATE, async_state_update) + + async def async_will_remove_from_hass(self): + """Register update dispatcher.""" + if self._unsub_dispatcher is not None: + self._unsub_dispatcher() + self._unsub_dispatcher = None diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index f73c16b19040f5..7fdfc7865150fc 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -6,15 +6,18 @@ import aiohttp from hass_nabucasa.client import CloudClient as Interface +from homeassistant.core import callback from homeassistant.components.alexa import smart_home as alexa_sh from homeassistant.components.google_assistant import ( helpers as ga_h, smart_home as ga) from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES from homeassistant.helpers.typing import HomeAssistantType +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.util.aiohttp import MockRequest from . import utils -from .const import CONF_ENTITY_CONFIG, CONF_FILTER, DOMAIN +from .const import ( + CONF_ENTITY_CONFIG, CONF_FILTER, DOMAIN, DISPATCHER_REMOTE_UPDATE) from .prefs import CloudPreferences @@ -115,13 +118,19 @@ async def cleanups(self) -> None: self._alexa_config = None self._google_config = None - async def async_user_message( - self, identifier: str, title: str, message: str) -> None: + @callback + def user_message(self, identifier: str, title: str, message: str) -> None: """Create a message for user to UI.""" self._hass.components.persistent_notification.async_create( message, title, identifier ) + @callback + def dispatcher_message(self, identifier: str, data: Any = None) -> None: + """Match cloud notification to dispatcher.""" + if identifier.startwith("remote_"): + async_dispatcher_send(self._hass, DISPATCHER_REMOTE_UPDATE, data) + async def async_alexa_message( self, payload: Dict[Any, Any]) -> Dict[Any, Any]: """Process cloud alexa message to client.""" diff --git a/homeassistant/components/cloud/const.py b/homeassistant/components/cloud/const.py index fdedacd6dbbef6..2816e3f6dc9b8d 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -25,3 +25,5 @@ MODE_DEV = "development" MODE_PROD = "production" + +DISPATCHER_REMOTE_UPDATE = 'cloud_remote_update' diff --git a/requirements_all.txt b/requirements_all.txt index 4f8d598665bb1a..a5f728ea232b95 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -524,7 +524,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.5 +hass-nabucasa==0.7 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 36f94167565a8e..65993daefa7a03 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.5 +hass-nabucasa==0.7 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py new file mode 100644 index 00000000000000..938829b809bdb6 --- /dev/null +++ b/tests/components/cloud/test_binary_sensor.py @@ -0,0 +1,32 @@ +"""Tests for the cloud binary sensor.""" +from unittest.mock import Mock + +from homeassistant.setup import async_setup_component +from homeassistant.components.cloud.const import DISPATCHER_REMOTE_UPDATE + + +async def test_remote_connection_sensor(hass): + """Test the remote connection sensor.""" + assert await async_setup_component(hass, 'cloud', {'cloud': {}}) + cloud = hass.data['cloud'] = Mock() + cloud.remote.certificate = None + await hass.async_block_till_done() + + state = hass.states.get('binary_sensor.remote_ui') + assert state is not None + assert state.state == 'unavailable' + + cloud.remote.is_connected = False + cloud.remote.certificate = object() + hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + await hass.async_block_till_done() + + state = hass.states.get('binary_sensor.remote_ui') + assert state.state == 'off' + + cloud.remote.is_connected = True + hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + await hass.async_block_till_done() + + state = hass.states.get('binary_sensor.remote_ui') + assert state.state == 'on' From b18aef8d317563700482baca9d5ac41670f590ea Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 07:47:13 -0700 Subject: [PATCH 022/605] Fix test --- tests/components/mobile_app/test_entity.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index d8cb91a8bc61ca..f399f84274503a 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -37,11 +37,8 @@ async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401 json = await reg_resp.json() assert json == {'status': 'registered'} - # 3 because we require device_tracker which adds zone.home and - # group.all_devices - assert len(hass.states.async_all()) == 3 - - entity = hass.states.async_all()[2] + entity = hass.states.get('sensor.battery_state') + assert entity is not None assert entity.attributes['device_class'] == 'battery' assert entity.attributes['icon'] == 'mdi:battery' From 3d404c43c800df095bd5a30c25bb5c8fcde01541 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 09:14:20 -0700 Subject: [PATCH 023/605] Fix more test --- tests/components/mobile_app/test_entity.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index f399f84274503a..5dc285cfe9ed45 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -65,8 +65,7 @@ async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401 assert update_resp.status == 200 - updated_entity = hass.states.async_all()[2] - + updated_entity = hass.states.get('sensor.battery_state') assert updated_entity.state == '123' From ff6b86b5a825bee623e88cd2e12fa04c179e1480 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 10:59:55 -0700 Subject: [PATCH 024/605] Bumped version to 0.90.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index b1a0fbb1e4f358..8fa218efd8694c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b1' +PATCH_VERSION = '0b2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From aa81819683110093058058b3625b5579ebfb629d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 11:11:59 -0700 Subject: [PATCH 025/605] Fix func --- homeassistant/components/cloud/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index 7fdfc7865150fc..da89f8331a9536 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -128,7 +128,7 @@ def user_message(self, identifier: str, title: str, message: str) -> None: @callback def dispatcher_message(self, identifier: str, data: Any = None) -> None: """Match cloud notification to dispatcher.""" - if identifier.startwith("remote_"): + if identifier.startswith("remote_"): async_dispatcher_send(self._hass, DISPATCHER_REMOTE_UPDATE, data) async def async_alexa_message( From 592447927258ac196e442e351cb0cc626fcb20d4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 23:17:41 -0700 Subject: [PATCH 026/605] Updated frontend to 20190315.1 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index a8d2cbc35b9dc9..5d5585ddd23c6f 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190315.0'] +REQUIREMENTS = ['home-assistant-frontend==20190315.1'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index a5f728ea232b95..d2cdfad49bb17c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190315.0 +home-assistant-frontend==20190315.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 65993daefa7a03..752ee21d614266 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190315.0 +home-assistant-frontend==20190315.1 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From a46b64d227a0e8635a8fc3c84b1d2697892212d3 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Fri, 15 Mar 2019 12:25:09 -0700 Subject: [PATCH 027/605] Bump androidtv to 0.0.12 (#22072) --- homeassistant/components/androidtv/media_player.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 458fdff87fd91b..1282a40cac5a54 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.11'] +REQUIREMENTS = ['androidtv==0.0.12'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d2cdfad49bb17c..22509e84f7c9e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -158,7 +158,7 @@ alpha_vantage==2.1.0 amcrest==1.2.5 # homeassistant.components.androidtv.media_player -androidtv==0.0.11 +androidtv==0.0.12 # homeassistant.components.switch.anel_pwrctrl anel_pwrctrl-homeassistant==0.0.1.dev2 From 68d1a5322a4adfb1ff5e7eb71485a5e7627519bb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 19:26:10 -0700 Subject: [PATCH 028/605] Prevent cloud remote UI when using 127.0.0.1 as trusted network (#22093) * Prevent cloud remote UI when using trusted networks * Limit to 127.0.0.1 trusted network * Update error msg * Disable ipv6 loopback --- homeassistant/components/cloud/const.py | 4 + homeassistant/components/cloud/http_api.py | 73 +++++++++------- homeassistant/components/cloud/prefs.py | 35 +++++++- tests/components/cloud/test_http_api.py | 99 ++++++++++++++++++++++ 4 files changed, 177 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/cloud/const.py b/homeassistant/components/cloud/const.py index 2816e3f6dc9b8d..1286832c0c7abf 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -27,3 +27,7 @@ MODE_PROD = "production" DISPATCHER_REMOTE_UPDATE = 'cloud_remote_update' + + +class InvalidTrustedNetworks(Exception): + """Raised when invalid trusted networks config.""" diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 61b3b8576eccc7..212bdfb4bf8ae5 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -18,7 +18,7 @@ from .const import ( DOMAIN, REQUEST_TIMEOUT, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, - PREF_GOOGLE_ALLOW_UNLOCK) + PREF_GOOGLE_ALLOW_UNLOCK, InvalidTrustedNetworks) _LOGGER = logging.getLogger(__name__) @@ -58,7 +58,11 @@ }) -_CLOUD_ERRORS = {} +_CLOUD_ERRORS = { + InvalidTrustedNetworks: + (500, 'Remote UI not compatible with 127.0.0.1/::1' + ' as a trusted network.') +} async def async_setup(hass): @@ -106,7 +110,9 @@ async def async_setup(hass): auth.PasswordChangeRequired: (400, 'Password change required.'), asyncio.TimeoutError: - (502, 'Unable to reach the Home Assistant cloud.') + (502, 'Unable to reach the Home Assistant cloud.'), + aiohttp.ClientError: + (500, 'Error making internal request'), }) @@ -120,12 +126,7 @@ async def error_handler(view, request, *args, **kwargs): return result except Exception as err: # pylint: disable=broad-except - err_info = _CLOUD_ERRORS.get(err.__class__) - if err_info is None: - _LOGGER.exception( - "Unexpected error processing request for %s", request.path) - err_info = (502, 'Unexpected error: {}'.format(err)) - status, msg = err_info + status, msg = _process_cloud_exception(err, request.path) return view.json_message( msg, status_code=status, message_code=err.__class__.__name__.lower()) @@ -133,6 +134,31 @@ async def error_handler(view, request, *args, **kwargs): return error_handler +def _ws_handle_cloud_errors(handler): + """Websocket decorator to handle auth errors.""" + @wraps(handler) + async def error_handler(hass, connection, msg): + """Handle exceptions that raise from the wrapped handler.""" + try: + return await handler(hass, connection, msg) + + except Exception as err: # pylint: disable=broad-except + err_status, err_msg = _process_cloud_exception(err, msg['type']) + connection.send_error(msg['id'], err_status, err_msg) + + return error_handler + + +def _process_cloud_exception(exc, where): + """Process a cloud exception.""" + err_info = _CLOUD_ERRORS.get(exc.__class__) + if err_info is None: + _LOGGER.exception( + "Unexpected error processing request for %s", where) + err_info = (502, 'Unexpected error: {}'.format(exc)) + return err_info + + class GoogleActionsSyncView(HomeAssistantView): """Trigger a Google Actions Smart Home Sync.""" @@ -295,26 +321,6 @@ def with_cloud_auth(hass, connection, msg): return with_cloud_auth -def _handle_aiohttp_errors(handler): - """Websocket decorator that handlers aiohttp errors. - - Can only wrap async handlers. - """ - @wraps(handler) - async def with_error_handling(hass, connection, msg): - """Handle aiohttp errors.""" - try: - await handler(hass, connection, msg) - except asyncio.TimeoutError: - connection.send_message(websocket_api.error_message( - msg['id'], 'timeout', 'Command timed out.')) - except aiohttp.ClientError: - connection.send_message(websocket_api.error_message( - msg['id'], 'unknown', 'Error making request.')) - - return with_error_handling - - @_require_cloud_login @websocket_api.async_response async def websocket_subscription(hass, connection, msg): @@ -363,7 +369,7 @@ async def websocket_update_prefs(hass, connection, msg): @_require_cloud_login @websocket_api.async_response -@_handle_aiohttp_errors +@_ws_handle_cloud_errors async def websocket_hook_create(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -373,6 +379,7 @@ async def websocket_hook_create(hass, connection, msg): @_require_cloud_login @websocket_api.async_response +@_ws_handle_cloud_errors async def websocket_hook_delete(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -417,25 +424,27 @@ def _account_data(cloud): @_require_cloud_login @websocket_api.async_response +@_ws_handle_cloud_errors @websocket_api.websocket_command({ 'type': 'cloud/remote/connect' }) async def websocket_remote_connect(hass, connection, msg): """Handle request for connect remote.""" cloud = hass.data[DOMAIN] - await cloud.remote.connect() await cloud.client.prefs.async_update(remote_enabled=True) + await cloud.remote.connect() connection.send_result(msg['id'], _account_data(cloud)) @_require_cloud_login @websocket_api.async_response +@_ws_handle_cloud_errors @websocket_api.websocket_command({ 'type': 'cloud/remote/disconnect' }) async def websocket_remote_disconnect(hass, connection, msg): """Handle request for disconnect remote.""" cloud = hass.data[DOMAIN] - await cloud.remote.disconnect() await cloud.client.prefs.async_update(remote_enabled=False) + await cloud.remote.disconnect() connection.send_result(msg['id'], _account_data(cloud)) diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index 16ff8f0c213309..b0244f6b1fb162 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -1,7 +1,10 @@ """Preference management for cloud.""" +from ipaddress import ip_address + from .const import ( DOMAIN, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, PREF_ENABLE_REMOTE, - PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS, PREF_CLOUD_USER) + PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS, PREF_CLOUD_USER, + InvalidTrustedNetworks) STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 @@ -13,6 +16,7 @@ class CloudPreferences: def __init__(self, hass): """Initialize cloud prefs.""" + self._hass = hass self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) self._prefs = None @@ -48,6 +52,9 @@ async def async_update(self, *, google_enabled=_UNDEF, if value is not _UNDEF: self._prefs[key] = value + if remote_enabled is True and self._has_local_trusted_network: + raise InvalidTrustedNetworks + await self._store.async_save(self._prefs) def as_dict(self): @@ -57,7 +64,15 @@ def as_dict(self): @property def remote_enabled(self): """Return if remote is enabled on start.""" - return self._prefs.get(PREF_ENABLE_REMOTE, False) + enabled = self._prefs.get(PREF_ENABLE_REMOTE, False) + + if not enabled: + return False + + if self._has_local_trusted_network: + return False + + return True @property def alexa_enabled(self): @@ -83,3 +98,19 @@ def cloudhooks(self): def cloud_user(self) -> str: """Return ID from Home Assistant Cloud system user.""" return self._prefs.get(PREF_CLOUD_USER) + + @property + def _has_local_trusted_network(self) -> bool: + """Return if we allow localhost to bypass auth.""" + local4 = ip_address('127.0.0.1') + local6 = ip_address('::1') + + for prv in self._hass.auth.auth_providers: + if prv.type != 'trusted_networks': + continue + + for network in prv.trusted_networks: + if local4 in network or local6 in network: + return True + + return False diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 3ab4b1030fa081..6c50a158cad30b 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -7,6 +7,7 @@ from hass_nabucasa.auth import Unauthenticated, UnknownError from hass_nabucasa.const import STATE_CONNECTED +from homeassistant.auth.providers import trusted_networks as tn_auth from homeassistant.components.cloud.const import ( PREF_ENABLE_GOOGLE, PREF_ENABLE_ALEXA, PREF_GOOGLE_ALLOW_UNLOCK, DOMAIN) @@ -589,3 +590,101 @@ async def test_disabling_remote(hass, hass_ws_client, setup_api, assert not cloud.client.remote_autostart assert len(mock_disconnect.mock_calls) == 1 + + +async def test_enabling_remote_trusted_networks_local4( + hass, hass_ws_client, setup_api, mock_cloud_login): + """Test we cannot enable remote UI when trusted networks active.""" + hass.auth._providers[('trusted_networks', None)] = \ + tn_auth.TrustedNetworksAuthProvider( + hass, None, tn_auth.CONFIG_SCHEMA({ + 'type': 'trusted_networks', + 'trusted_networks': [ + '127.0.0.1' + ] + }) + ) + + client = await hass_ws_client(hass) + + with patch( + 'hass_nabucasa.remote.RemoteUI.connect', + side_effect=AssertionError + ) as mock_connect: + await client.send_json({ + 'id': 5, + 'type': 'cloud/remote/connect', + }) + response = await client.receive_json() + + assert not response['success'] + assert response['error']['code'] == 500 + assert response['error']['message'] == \ + 'Remote UI not compatible with 127.0.0.1/::1 as a trusted network.' + + assert len(mock_connect.mock_calls) == 0 + + +async def test_enabling_remote_trusted_networks_local6( + hass, hass_ws_client, setup_api, mock_cloud_login): + """Test we cannot enable remote UI when trusted networks active.""" + hass.auth._providers[('trusted_networks', None)] = \ + tn_auth.TrustedNetworksAuthProvider( + hass, None, tn_auth.CONFIG_SCHEMA({ + 'type': 'trusted_networks', + 'trusted_networks': [ + '::1' + ] + }) + ) + + client = await hass_ws_client(hass) + + with patch( + 'hass_nabucasa.remote.RemoteUI.connect', + side_effect=AssertionError + ) as mock_connect: + await client.send_json({ + 'id': 5, + 'type': 'cloud/remote/connect', + }) + response = await client.receive_json() + + assert not response['success'] + assert response['error']['code'] == 500 + assert response['error']['message'] == \ + 'Remote UI not compatible with 127.0.0.1/::1 as a trusted network.' + + assert len(mock_connect.mock_calls) == 0 + + +async def test_enabling_remote_trusted_networks_other( + hass, hass_ws_client, setup_api, mock_cloud_login): + """Test we cannot enable remote UI when trusted networks active.""" + hass.auth._providers[('trusted_networks', None)] = \ + tn_auth.TrustedNetworksAuthProvider( + hass, None, tn_auth.CONFIG_SCHEMA({ + 'type': 'trusted_networks', + 'trusted_networks': [ + '192.168.0.0/24' + ] + }) + ) + + client = await hass_ws_client(hass) + cloud = hass.data[DOMAIN] + + with patch( + 'hass_nabucasa.remote.RemoteUI.connect', + return_value=mock_coro() + ) as mock_connect: + await client.send_json({ + 'id': 5, + 'type': 'cloud/remote/connect', + }) + response = await client.receive_json() + + assert response['success'] + assert cloud.client.remote_autostart + + assert len(mock_connect.mock_calls) == 1 From 7a88c58ffa54c19965c7726941f8e106849a3ce6 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 16 Mar 2019 02:19:32 -0400 Subject: [PATCH 029/605] Beta Fix: FFMPEG and Stream component (#22091) * remove stream_source from ffmpeg and onvif and add to generic ip cam * fix tests --- homeassistant/components/camera/ffmpeg.py | 5 ----- homeassistant/components/camera/generic.py | 8 ++++++++ homeassistant/components/camera/onvif.py | 5 ----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 83ffdd499e9553..db9e73f3e1bcac 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -76,8 +76,3 @@ async def handle_async_mjpeg_stream(self, request): def name(self): """Return the name of this camera.""" return self._name - - @property - def stream_source(self): - """Return the source of the stream.""" - return self._input diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index ae7e849c234d1e..c8d6721ac18af2 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -28,12 +28,14 @@ CONF_CONTENT_TYPE = 'content_type' CONF_LIMIT_REFETCH_TO_URL_CHANGE = 'limit_refetch_to_url_change' CONF_STILL_IMAGE_URL = 'still_image_url' +CONF_STREAM_SOURCE = 'stream_source' CONF_FRAMERATE = 'framerate' DEFAULT_NAME = 'Generic Camera' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_STILL_IMAGE_URL): cv.template, + vol.Optional(CONF_STREAM_SOURCE, default=None): vol.Any(None, cv.string), vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]), vol.Optional(CONF_LIMIT_REFETCH_TO_URL_CHANGE, default=False): cv.boolean, @@ -62,6 +64,7 @@ def __init__(self, hass, device_info): self._authentication = device_info.get(CONF_AUTHENTICATION) self._name = device_info.get(CONF_NAME) self._still_image_url = device_info[CONF_STILL_IMAGE_URL] + self._stream_source = device_info[CONF_STREAM_SOURCE] self._still_image_url.hass = hass self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE] self._frame_interval = 1 / device_info[CONF_FRAMERATE] @@ -141,3 +144,8 @@ def fetch(): def name(self): """Return the name of this device.""" return self._name + + @property + def stream_source(self): + """Return the source of the stream.""" + return self._stream_source diff --git a/homeassistant/components/camera/onvif.py b/homeassistant/components/camera/onvif.py index b0bd029a80ca6c..da0bae7c50bacb 100644 --- a/homeassistant/components/camera/onvif.py +++ b/homeassistant/components/camera/onvif.py @@ -230,8 +230,3 @@ async def handle_async_mjpeg_stream(self, request): def name(self): """Return the name of this camera.""" return self._name - - @property - def stream_source(self): - """Return the source of the stream.""" - return self._input From 7b224dde23b12acdc6d75647c49ea1c480693b59 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 15 Mar 2019 23:20:19 -0700 Subject: [PATCH 030/605] Bumped version to 0.90.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 8fa218efd8694c..d0c6a72f5e5a8a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b2' +PATCH_VERSION = '0b3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 16ac1d4600b5461969457251be3d10303ee24eef Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 16 Mar 2019 23:23:28 -0700 Subject: [PATCH 031/605] Updated frontend to 20190316.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 5d5585ddd23c6f..286ece850a6ed0 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190315.1'] +REQUIREMENTS = ['home-assistant-frontend==20190316.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 22509e84f7c9e9..4feca7efebae73 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190315.1 +home-assistant-frontend==20190316.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 752ee21d614266..38b478f78b2175 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190315.1 +home-assistant-frontend==20190316.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From f21856418518811bb9a002202e49535dfeeb852f Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 17 Mar 2019 02:16:05 -0400 Subject: [PATCH 032/605] delete previously removed service option from services yaml (#22123) --- homeassistant/components/camera/services.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/homeassistant/components/camera/services.yaml b/homeassistant/components/camera/services.yaml index ec00ce3ef5c390..575f1fe76f7d1f 100644 --- a/homeassistant/components/camera/services.yaml +++ b/homeassistant/components/camera/services.yaml @@ -50,9 +50,6 @@ play_stream: format: description: (Optional) Stream format supported by media player. example: 'hls' - keepalive: - description: (Optional) Keep the stream worker alive for fast access. - example: 'true' local_file_update_file_path: description: Update the file_path for a local_file camera. From 872ee3eb210cbb404d5be897ca00cb51ce66ac42 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 16 Mar 2019 23:26:48 -0700 Subject: [PATCH 033/605] Bumped version to 0.90.0b4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d0c6a72f5e5a8a..1838e02eb23634 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b3' +PATCH_VERSION = '0b4' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From c37dcacf54f9cf127144712f02d77a5c25bb2faf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 18 Mar 2019 16:54:31 -0700 Subject: [PATCH 034/605] Updated frontend to 20190318.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 286ece850a6ed0..96619face25bff 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190316.0'] +REQUIREMENTS = ['home-assistant-frontend==20190318.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 4feca7efebae73..3138d3754aed7e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190316.0 +home-assistant-frontend==20190318.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 38b478f78b2175..cfdd432baa71bf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190316.0 +home-assistant-frontend==20190318.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 22624715a9af7bb2fccda4ecc685f44b26fb3c53 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 17 Mar 2019 03:42:49 -0700 Subject: [PATCH 035/605] Remove hass.config from aws_lambda notify payload (#22125) --- homeassistant/components/notify/aws_lambda.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index 28fedf6434dc53..17df1ba8f5aa0c 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -39,8 +39,7 @@ def get_service(hass, config, discovery_info=None): """Get the AWS Lambda notification service.""" - context_str = json.dumps({'hass': hass.config.as_dict(), - 'custom': config[CONF_CONTEXT]}, cls=JSONEncoder) + context_str = json.dumps({'custom': config[CONF_CONTEXT]}, cls=JSONEncoder) context_b64 = base64.b64encode(context_str.encode('utf-8')) context = context_b64.decode('utf-8') From cc00f3cd2ec0ae74dd256aa97072374434baf1de Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 17 Mar 2019 19:13:06 -0700 Subject: [PATCH 036/605] Allow non-admins to listen to certain events (#22137) --- .../components/websocket_api/commands.py | 39 +++++++--- .../components/websocket_api/permissions.py | 23 ++++++ .../components/websocket_api/test_commands.py | 73 +++++++++++++++++++ 3 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 homeassistant/components/websocket_api/permissions.py diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index b64fac0ed5144b..32bbd90aad1eb9 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -1,7 +1,9 @@ """Commands part of Websocket API.""" import voluptuous as vol -from homeassistant.const import MATCH_ALL, EVENT_TIME_CHANGED +from homeassistant.auth.permissions.const import POLICY_READ +from homeassistant.const import ( + MATCH_ALL, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED) from homeassistant.core import callback, DOMAIN as HASS_DOMAIN from homeassistant.exceptions import Unauthorized, ServiceNotFound, \ HomeAssistantError @@ -42,20 +44,37 @@ def handle_subscribe_events(hass, connection, msg): Async friendly. """ - if not connection.user.is_admin: + from .permissions import SUBSCRIBE_WHITELIST + + event_type = msg['event_type'] + + if (event_type not in SUBSCRIBE_WHITELIST and + not connection.user.is_admin): raise Unauthorized - async def forward_events(event): - """Forward events to websocket.""" - if event.event_type == EVENT_TIME_CHANGED: - return + if event_type == EVENT_STATE_CHANGED: + @callback + def forward_events(event): + """Forward state changed events to websocket.""" + if not connection.user.permissions.check_entity( + event.data['entity_id'], POLICY_READ): + return + + connection.send_message(messages.event_message(msg['id'], event)) + + else: + @callback + def forward_events(event): + """Forward events to websocket.""" + if event.event_type == EVENT_TIME_CHANGED: + return - connection.send_message(messages.event_message( - msg['id'], event.as_dict() - )) + connection.send_message(messages.event_message( + msg['id'], event.as_dict() + )) connection.subscriptions[msg['id']] = hass.bus.async_listen( - msg['event_type'], forward_events) + event_type, forward_events) connection.send_message(messages.result_message(msg['id'])) diff --git a/homeassistant/components/websocket_api/permissions.py b/homeassistant/components/websocket_api/permissions.py new file mode 100644 index 00000000000000..b98b21d184e0a0 --- /dev/null +++ b/homeassistant/components/websocket_api/permissions.py @@ -0,0 +1,23 @@ +"""Permission constants for the websocket API. + +Separate file to avoid circular imports. +""" +from homeassistant.const import ( + EVENT_COMPONENT_LOADED, + EVENT_SERVICE_REGISTERED, + EVENT_SERVICE_REMOVED, + EVENT_STATE_CHANGED, + EVENT_THEMES_UPDATED) +from homeassistant.components.persistent_notification import ( + EVENT_PERSISTENT_NOTIFICATIONS_UPDATED) + +# These are events that do not contain any sensitive data +# Except for state_changed, which is handled accordingly. +SUBSCRIBE_WHITELIST = { + EVENT_COMPONENT_LOADED, + EVENT_PERSISTENT_NOTIFICATIONS_UPDATED, + EVENT_SERVICE_REGISTERED, + EVENT_SERVICE_REMOVED, + EVENT_STATE_CHANGED, + EVENT_THEMES_UPDATED, +} diff --git a/tests/components/websocket_api/test_commands.py b/tests/components/websocket_api/test_commands.py index 8e0f751abedc32..4f3be31b22cd2d 100644 --- a/tests/components/websocket_api/test_commands.py +++ b/tests/components/websocket_api/test_commands.py @@ -333,3 +333,76 @@ async def test_get_states_not_allows_nan(hass, websocket_client): msg = await websocket_client.receive_json() assert not msg['success'] assert msg['error']['code'] == const.ERR_UNKNOWN_ERROR + + +async def test_subscribe_unsubscribe_events_whitelist( + hass, websocket_client, hass_admin_user): + """Test subscribe/unsubscribe events on whitelist.""" + hass_admin_user.groups = [] + + await websocket_client.send_json({ + 'id': 5, + 'type': 'subscribe_events', + 'event_type': 'not-in-whitelist' + }) + + msg = await websocket_client.receive_json() + assert msg['id'] == 5 + assert msg['type'] == const.TYPE_RESULT + assert not msg['success'] + assert msg['error']['code'] == 'unauthorized' + + await websocket_client.send_json({ + 'id': 6, + 'type': 'subscribe_events', + 'event_type': 'themes_updated' + }) + + msg = await websocket_client.receive_json() + assert msg['id'] == 6 + assert msg['type'] == const.TYPE_RESULT + assert msg['success'] + + hass.bus.async_fire('themes_updated') + + with timeout(3, loop=hass.loop): + msg = await websocket_client.receive_json() + + assert msg['id'] == 6 + assert msg['type'] == 'event' + event = msg['event'] + assert event['event_type'] == 'themes_updated' + assert event['origin'] == 'LOCAL' + + +async def test_subscribe_unsubscribe_events_state_changed( + hass, websocket_client, hass_admin_user): + """Test subscribe/unsubscribe state_changed events.""" + hass_admin_user.groups = [] + hass_admin_user.mock_policy({ + 'entities': { + 'entity_ids': { + 'light.permitted': True + } + } + }) + + await websocket_client.send_json({ + 'id': 7, + 'type': 'subscribe_events', + 'event_type': 'state_changed' + }) + + msg = await websocket_client.receive_json() + assert msg['id'] == 7 + assert msg['type'] == const.TYPE_RESULT + assert msg['success'] + + hass.states.async_set('light.not_permitted', 'on') + hass.states.async_set('light.permitted', 'on') + + msg = await websocket_client.receive_json() + assert msg['id'] == 7 + assert msg['type'] == 'event' + assert msg['event']['event_type'] == 'state_changed' + assert msg['event']['data']['entity_id'] == 'light.permitted' From 33a70758838374b437386ffeace62d38dba039d2 Mon Sep 17 00:00:00 2001 From: WebSpider Date: Mon, 18 Mar 2019 13:54:24 +0100 Subject: [PATCH 037/605] Bump tado version (#22145) * Bump python-tado, new API endpoint * Change references of old API endpoint to new * Update REQUIREMENTS --- homeassistant/components/tado/__init__.py | 2 +- homeassistant/components/tado/device_tracker.py | 4 ++-- requirements_all.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 767e29ba0b9c3c..56fc0cb704c573 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,7 +10,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.util import Throttle -REQUIREMENTS = ['python-tado==0.2.3'] +REQUIREMENTS = ['python-tado==0.2.8'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tado/device_tracker.py b/homeassistant/components/tado/device_tracker.py index 7812bbd812bbd5..8804bef561622a 100644 --- a/homeassistant/components/tado/device_tracker.py +++ b/homeassistant/components/tado/device_tracker.py @@ -52,9 +52,9 @@ def __init__(self, hass, config): # If there's a home_id, we need a different API URL if self.home_id is None: - self.tadoapiurl = 'https://my.tado.com/api/v2/me' + self.tadoapiurl = 'https://auth.tado.com/api/v2/me' else: - self.tadoapiurl = 'https://my.tado.com/api/v2' \ + self.tadoapiurl = 'https://auth.tado.com/api/v2' \ '/homes/{home_id}/mobileDevices' # The API URL always needs a username and password diff --git a/requirements_all.txt b/requirements_all.txt index 3138d3754aed7e..d07c4addef4e83 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1394,7 +1394,7 @@ python-songpal==0.0.9.1 python-synology==0.2.0 # homeassistant.components.tado -python-tado==0.2.3 +python-tado==0.2.8 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 From 1c9b750e36c4141f20b04935840f55a92e902477 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Mon, 18 Mar 2019 09:27:34 -0400 Subject: [PATCH 038/605] Fix resetting access token on streams with keepalive (#22148) --- homeassistant/components/stream/__init__.py | 6 ++++++ homeassistant/components/stream/core.py | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 1d04791a11ab92..3f715af0e047d4 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -120,10 +120,16 @@ def remove_provider(self, provider): """Remove provider output stream.""" if provider.format in self._outputs: del self._outputs[provider.format] + self.check_idle() if not self._outputs: self.stop() + def check_idle(self): + """Reset access token if all providers are idle.""" + if all([p.idle for p in self._outputs.values()]): + self.access_token = None + def start(self): """Start a stream.""" if self._thread is None or not self._thread.isAlive(): diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 3d6ffa0e20c4e8..665803d38ebcab 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -43,6 +43,7 @@ class StreamOutput: def __init__(self, stream) -> None: """Initialize a stream output.""" + self.idle = False self._stream = stream self._cursor = None self._event = asyncio.Event() @@ -77,10 +78,11 @@ def target_duration(self) -> int: def get_segment(self, sequence: int = None) -> Any: """Retrieve a specific segment, or the whole list.""" + self.idle = False # Reset idle timeout if self._unsub is not None: self._unsub() - self._unsub = async_call_later(self._stream.hass, 300, self._cleanup) + self._unsub = async_call_later(self._stream.hass, 300, self._timeout) if not sequence: return self._segments @@ -109,7 +111,7 @@ def put(self, segment: Segment) -> None: # Start idle timeout when we start recieving data if self._unsub is None: self._unsub = async_call_later( - self._stream.hass, 300, self._cleanup) + self._stream.hass, 300, self._timeout) if segment is None: self._event.set() @@ -124,7 +126,15 @@ def put(self, segment: Segment) -> None: self._event.clear() @callback - def _cleanup(self, _now=None): + def _timeout(self, _now=None): + """Handle stream timeout.""" + if self._stream.keepalive: + self.idle = True + self._stream.check_idle() + else: + self._cleanup() + + def _cleanup(self): """Remove provider.""" self._segments = [] self._stream.remove_provider(self) From d75d75e49fd0b68de3deb2697b7eb8b1205085d9 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 19 Mar 2019 00:58:48 +0100 Subject: [PATCH 039/605] Remove config check over supervisor (#22156) * Remove config check over supervisor * Fix lint * Fix tests --- homeassistant/components/hassio/__init__.py | 28 ++++---------- homeassistant/components/hassio/handler.py | 7 ---- tests/components/hassio/test_handler.py | 11 ------ tests/components/hassio/test_init.py | 41 +++++---------------- 4 files changed, 18 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index e070c889f31681..7f85c8cfc3fc66 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -7,6 +7,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components import SERVICE_CHECK_CONFIG +import homeassistant.config as conf_util from homeassistant.const import ( ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP) from homeassistant.core import DOMAIN as HASS_DOMAIN, callback @@ -130,23 +131,6 @@ def is_hassio(hass): return DOMAIN in hass.config.components -@bind_hass -async def async_check_config(hass): - """Check configuration over Hass.io API.""" - hassio = hass.data[DOMAIN] - - try: - result = await hassio.check_homeassistant_config() - except HassioAPIError as err: - _LOGGER.error("Error on Hass.io API: %s", err) - raise HomeAssistantError() from None - else: - if result['result'] == "error": - return result['message'] - - return None - - async def async_setup(hass, config): """Set up the Hass.io component.""" # Check local setup @@ -259,9 +243,13 @@ async def async_handle_core_service(call): await hassio.stop_homeassistant() return - error = await async_check_config(hass) - if error: - _LOGGER.error(error) + try: + errors = await conf_util.async_check_ha_config_file(hass) + except HomeAssistantError: + return + + if errors: + _LOGGER.error(errors) hass.components.persistent_notification.async_create( "Config error. See dev-info panel for details.", "Config validating", "{0}.check_config".format(HASS_DOMAIN)) diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 46e32c9f7c329e..7eb3245c0df5d6 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -97,13 +97,6 @@ def stop_homeassistant(self): """ return self.send_command("/homeassistant/stop") - def check_homeassistant_config(self): - """Check Home-Assistant config with Hass.io API. - - This method return a coroutine. - """ - return self.send_command("/homeassistant/check", timeout=600) - @_api_data def retrieve_discovery_messages(self): """Return all discovery data from Hass.io API. diff --git a/tests/components/hassio/test_handler.py b/tests/components/hassio/test_handler.py index db3917a220127d..3e7b9e95d92432 100644 --- a/tests/components/hassio/test_handler.py +++ b/tests/components/hassio/test_handler.py @@ -74,17 +74,6 @@ async def test_api_homeassistant_restart(hassio_handler, aioclient_mock): assert aioclient_mock.call_count == 1 -async def test_api_homeassistant_config(hassio_handler, aioclient_mock): - """Test setup with API HomeAssistant config.""" - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={ - 'result': 'ok', 'data': {'test': 'bla'}}) - - data = await hassio_handler.check_homeassistant_config() - assert data['data']['test'] == 'bla' - assert aioclient_mock.call_count == 1 - - async def test_api_addon_info(hassio_handler, aioclient_mock): """Test setup with API Add-on info.""" aioclient_mock.get( diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 435e03a1755784..1326805fc93944 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -7,8 +7,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.setup import async_setup_component -from homeassistant.components.hassio import ( - STORAGE_KEY, async_check_config) +from homeassistant.components.hassio import STORAGE_KEY from tests.common import mock_coro @@ -311,8 +310,6 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock): "http://127.0.0.1/homeassistant/restart", json={'result': 'ok'}) aioclient_mock.post( "http://127.0.0.1/homeassistant/stop", json={'result': 'ok'}) - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) yield from hass.services.async_call('homeassistant', 'stop') yield from hass.async_block_till_done() @@ -322,32 +319,14 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock): yield from hass.services.async_call('homeassistant', 'check_config') yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 3 - - yield from hass.services.async_call('homeassistant', 'restart') - yield from hass.async_block_till_done() - - assert aioclient_mock.call_count == 5 - - -@asyncio.coroutine -def test_check_config_ok(hassio_env, hass, aioclient_mock): - """Check Config that is okay.""" - assert (yield from async_setup_component(hass, 'hassio', {})) - - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={'result': 'ok'}) - - assert (yield from async_check_config(hass)) is None - - -@asyncio.coroutine -def test_check_config_fail(hassio_env, hass, aioclient_mock): - """Check Config that is wrong.""" - assert (yield from async_setup_component(hass, 'hassio', {})) + assert aioclient_mock.call_count == 2 - aioclient_mock.post( - "http://127.0.0.1/homeassistant/check", json={ - 'result': 'error', 'message': "Error"}) + with patch( + 'homeassistant.config.async_check_ha_config_file', + return_value=mock_coro() + ) as mock_check_config: + yield from hass.services.async_call('homeassistant', 'restart') + yield from hass.async_block_till_done() + assert mock_check_config.called - assert (yield from async_check_config(hass)) == "Error" + assert aioclient_mock.call_count == 3 From 592edd10ef518c6687b15e9f39a31fe9652cd46f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 19 Mar 2019 00:56:57 +0100 Subject: [PATCH 040/605] Upgrade toonapilib to 3.2.2 + lower interval (#22160) --- homeassistant/components/toon/__init__.py | 2 +- homeassistant/components/toon/binary_sensor.py | 2 +- homeassistant/components/toon/climate.py | 2 +- homeassistant/components/toon/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/toon/__init__.py b/homeassistant/components/toon/__init__.py index 0ca0a414fa569e..d718b5895e4520 100644 --- a/homeassistant/components/toon/__init__.py +++ b/homeassistant/components/toon/__init__.py @@ -16,7 +16,7 @@ CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DISPLAY, CONF_TENANT, DATA_TOON_CLIENT, DATA_TOON_CONFIG, DOMAIN) -REQUIREMENTS = ['toonapilib==3.2.1'] +REQUIREMENTS = ['toonapilib==3.2.2'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/toon/binary_sensor.py b/homeassistant/components/toon/binary_sensor.py index a50a67085ec7c3..694b7d1d03383b 100644 --- a/homeassistant/components/toon/binary_sensor.py +++ b/homeassistant/components/toon/binary_sensor.py @@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) -SCAN_INTERVAL = timedelta(seconds=30) +SCAN_INTERVAL = timedelta(seconds=300) async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry, diff --git a/homeassistant/components/toon/climate.py b/homeassistant/components/toon/climate.py index 13f1c1269a15d4..f09dc010c792ad 100644 --- a/homeassistant/components/toon/climate.py +++ b/homeassistant/components/toon/climate.py @@ -22,7 +22,7 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) -SCAN_INTERVAL = timedelta(seconds=30) +SCAN_INTERVAL = timedelta(seconds=300) HA_TOON = { STATE_AUTO: 'Comfort', diff --git a/homeassistant/components/toon/sensor.py b/homeassistant/components/toon/sensor.py index e263bda9fc7409..f58c8ef4840d66 100644 --- a/homeassistant/components/toon/sensor.py +++ b/homeassistant/components/toon/sensor.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) -SCAN_INTERVAL = timedelta(seconds=30) +SCAN_INTERVAL = timedelta(seconds=300) async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry, diff --git a/requirements_all.txt b/requirements_all.txt index d07c4addef4e83..b13da82945b4d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1707,7 +1707,7 @@ tikteck==0.4 todoist-python==7.0.17 # homeassistant.components.toon -toonapilib==3.2.1 +toonapilib==3.2.2 # homeassistant.components.alarm_control_panel.totalconnect total_connect_client==0.22 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cfdd432baa71bf..5cf0720d2ad3e2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -298,7 +298,7 @@ srpenergy==1.0.5 statsd==3.2.1 # homeassistant.components.toon -toonapilib==3.2.1 +toonapilib==3.2.2 # homeassistant.components.camera.uvc uvcclient==0.11.0 From ad0ec663538bc88f0ad0346dfbeaa17084ab7064 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 18 Mar 2019 17:04:49 -0700 Subject: [PATCH 041/605] Bumped version to 0.90.0b5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 1838e02eb23634..5a10155d7df758 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b4' +PATCH_VERSION = '0b5' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From fff6927f9ce98e7034346b5760642a6a640a9f15 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 11:38:05 -0700 Subject: [PATCH 042/605] Updated frontend to 20190319.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 96619face25bff..ee7795894614da 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190318.0'] +REQUIREMENTS = ['home-assistant-frontend==20190319.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index b13da82945b4d5..18452f1af828bf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190318.0 +home-assistant-frontend==20190319.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5cf0720d2ad3e2..c2f117997a4ef3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190318.0 +home-assistant-frontend==20190319.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From f202114ead9f29b3ac88fa4a5a03c41f94d2fb01 Mon Sep 17 00:00:00 2001 From: uchagani Date: Tue, 19 Mar 2019 03:51:42 -0400 Subject: [PATCH 043/605] bump total_connect_client to 0.24 (#22166) --- homeassistant/components/alarm_control_panel/totalconnect.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/totalconnect.py b/homeassistant/components/alarm_control_panel/totalconnect.py index 3b0725658d42ec..ba8155fde930f5 100644 --- a/homeassistant/components/alarm_control_panel/totalconnect.py +++ b/homeassistant/components/alarm_control_panel/totalconnect.py @@ -18,7 +18,7 @@ STATE_ALARM_ARMED_CUSTOM_BYPASS) -REQUIREMENTS = ['total_connect_client==0.22'] +REQUIREMENTS = ['total_connect_client==0.24'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 18452f1af828bf..ac407958cb8b57 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1710,7 +1710,7 @@ todoist-python==7.0.17 toonapilib==3.2.2 # homeassistant.components.alarm_control_panel.totalconnect -total_connect_client==0.22 +total_connect_client==0.24 # homeassistant.components.tplink_lte tp-connected==0.0.4 From b85189e6998a1d0bf4fc6ace2997c878c663566d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 19 Mar 2019 15:10:30 +0100 Subject: [PATCH 044/605] Update Hass-NabuCasa 0.8 (#22177) --- homeassistant/components/cloud/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 9f6e678e41737b..ff1b2344ac89c8 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.7'] +REQUIREMENTS = ['hass-nabucasa==0.8'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index ac407958cb8b57..a6d62468308b7e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -524,7 +524,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.7 +hass-nabucasa==0.8 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c2f117997a4ef3..5a357a5c0b3822 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.7 +hass-nabucasa==0.8 # homeassistant.components.mqtt.server hbmqtt==0.9.4 From e6ffc790f2a82dbe0a9f9c486c3bc17bee4c1ab5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 11:33:50 -0700 Subject: [PATCH 045/605] Always load Hass.io component on Hass.io (#22185) * Always load Hass.io component on Hass.io * Lint * Lint --- homeassistant/bootstrap.py | 22 +++++++++++++++---- .../components/discovery/__init__.py | 5 ----- tests/components/discovery/test_init.py | 16 -------------- tests/test_bootstrap.py | 11 +++++++++- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 444b4a9f855e78..3d05eb06e6c587 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -127,10 +127,7 @@ async def async_from_config_dict(config: Dict[str, Any], hass.config_entries = config_entries.ConfigEntries(hass, config) await hass.config_entries.async_initialize() - # Filter out the repeating and common config section [homeassistant] - components = set(key.split(' ')[0] for key in config.keys() - if key != core.DOMAIN) - components.update(hass.config_entries.async_domains()) + components = _get_components(hass, config) # Resolve all dependencies of all components. for component in list(components): @@ -391,3 +388,20 @@ async def async_mount_local_lib_path(config_dir: str) -> str: if lib_dir not in sys.path: sys.path.insert(0, lib_dir) return deps_dir + + +@core.callback +def _get_components(hass: core.HomeAssistant, config: Dict[str, Any]): + """Get components to set up.""" + # Filter out the repeating and common config section [homeassistant] + components = set(key.split(' ')[0] for key in config.keys() + if key != core.DOMAIN) + + # Add config entry domains + components.update(hass.config_entries.async_domains()) + + # Make sure the Hass.io component is loaded + if 'HASSIO' in os.environ: + components.add('hassio') + + return components diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 6a561e570c5a70..d4816213f50085 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -9,7 +9,6 @@ import json from datetime import timedelta import logging -import os import voluptuous as vol @@ -199,10 +198,6 @@ def schedule_first(event): """Schedule the first discovery when Home Assistant starts up.""" async_track_point_in_utc_time(hass, scan_devices, dt_util.utcnow()) - # Discovery for local services - if 'HASSIO' in os.environ: - hass.async_create_task(new_service_found(SERVICE_HASSIO, {})) - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_first) return True diff --git a/tests/components/discovery/test_init.py b/tests/components/discovery/test_init.py index d4566bc0b03b2f..28d30a9167fee6 100644 --- a/tests/components/discovery/test_init.py +++ b/tests/components/discovery/test_init.py @@ -1,6 +1,5 @@ """The tests for the discovery component.""" import asyncio -import os from unittest.mock import patch, MagicMock import pytest @@ -142,21 +141,6 @@ def discover(netdisco): SERVICE_NO_PLATFORM_COMPONENT, BASE_CONFIG) -@asyncio.coroutine -def test_load_component_hassio(hass): - """Test load hassio component.""" - def discover(netdisco): - """Fake discovery.""" - return [] - - with patch.dict(os.environ, {'HASSIO': "FAKE_HASSIO"}), \ - patch('homeassistant.components.hassio.async_setup', - return_value=mock_coro(return_value=True)) as mock_hassio: - yield from mock_discovery(hass, discover) - - assert mock_hassio.called - - async def test_discover_config_flow(hass): """Test discovery triggering a config flow.""" discovery_info = { diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 978b0b9d450cc2..1b62c5244e4024 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -34,7 +34,7 @@ def test_from_config_file(hass): } with patch_yaml_files(files, True): - yield from bootstrap.async_from_config_file('config.yaml') + yield from bootstrap.async_from_config_file('config.yaml', hass) assert components == hass.config.components @@ -103,3 +103,12 @@ async def test_async_from_config_file_not_mount_deps_folder(loop): await bootstrap.async_from_config_file('mock-path', hass) assert len(mock_mount.mock_calls) == 0 + + +async def test_load_hassio(hass): + """Test that we load Hass.io component.""" + with patch.dict(os.environ, {}, clear=True): + assert bootstrap._get_components(hass, {}) == set() + + with patch.dict(os.environ, {'HASSIO': '1'}): + assert bootstrap._get_components(hass, {}) == {'hassio'} From b8f246356aa0a98d853817697a9f4c8c692dc68f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 11:41:08 -0700 Subject: [PATCH 046/605] Bumped version to 0.90.0b6 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 5a10155d7df758..fa841c3f246c4b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b5' +PATCH_VERSION = '0b6' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 268d129ea9676800ce56f46105b6b3127e4fe62c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 14:04:19 -0700 Subject: [PATCH 047/605] Updated frontend to 20190319.1 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index ee7795894614da..5c061a7f8579bc 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190319.0'] +REQUIREMENTS = ['home-assistant-frontend==20190319.1'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index a6d62468308b7e..7aeed6d927893e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.0 +home-assistant-frontend==20190319.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5a357a5c0b3822..1875248a7a00ea 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.0 +home-assistant-frontend==20190319.1 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 7cf1f4f9fe4228645f9f876cfebea2181b6846e4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Mar 2019 16:48:31 -0700 Subject: [PATCH 048/605] Bumped version to 0.90.0b7 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fa841c3f246c4b..8a405b9a7bd1aa 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b6' +PATCH_VERSION = '0b7' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 398281959af62dc9b91086bf300a95c1226a332d Mon Sep 17 00:00:00 2001 From: Matt Snyder Date: Wed, 20 Mar 2019 05:33:42 -0500 Subject: [PATCH 049/605] Update codeowners (#22198) --- CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 9a4f38f60d0c2f..33853f1c08b914 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -179,6 +179,7 @@ homeassistant/components/counter/* @fabaff homeassistant/components/daikin/* @fredrike @rofrantz homeassistant/components/deconz/* @kane610 homeassistant/components/digital_ocean/* @fabaff +homeassistant/components/doorbird/* @oblogic7 homeassistant/components/dweet/* @fabaff # E @@ -228,6 +229,7 @@ homeassistant/components/no_ip/* @fabaff # O homeassistant/components/openuv/* @bachya +homeassistant/components/owlet/* @oblogic7 # P homeassistant/components/plant/* @ChristianKuehnel From 62c2bbd59ae986f1ae1c2b857ed9c0dd8b5a7be6 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Wed, 20 Mar 2019 22:49:27 +0800 Subject: [PATCH 050/605] Fixed typing errors (#22207) --- homeassistant/bootstrap.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 3d05eb06e6c587..d532d9cdb8685b 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -5,7 +5,7 @@ import sys from time import time from collections import OrderedDict -from typing import Any, Optional, Dict +from typing import Any, Optional, Dict, Set import voluptuous as vol @@ -391,14 +391,15 @@ async def async_mount_local_lib_path(config_dir: str) -> str: @core.callback -def _get_components(hass: core.HomeAssistant, config: Dict[str, Any]): +def _get_components(hass: core.HomeAssistant, + config: Dict[str, Any]) -> Set[str]: """Get components to set up.""" # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) # Add config entry domains - components.update(hass.config_entries.async_domains()) + components.update(hass.config_entries.async_domains()) # type: ignore # Make sure the Hass.io component is loaded if 'HASSIO' in os.environ: From 1bf49ce5a3712ff500904c446fa667764b3913e0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 20 Mar 2019 07:45:09 -0700 Subject: [PATCH 051/605] Updated frontend to 20190320.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 5c061a7f8579bc..6e05299ec52b6c 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190319.1'] +REQUIREMENTS = ['home-assistant-frontend==20190320.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 7aeed6d927893e..87e9f53bba9ffc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.1 +home-assistant-frontend==20190320.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1875248a7a00ea..935bc5689e1832 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.1 +home-assistant-frontend==20190320.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From d4cd39e43efd223ccc4430ae7f63527e8d5c7837 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Wed, 20 Mar 2019 22:49:27 +0800 Subject: [PATCH 052/605] Fixed typing errors (#22207) --- homeassistant/bootstrap.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 3d05eb06e6c587..d532d9cdb8685b 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -5,7 +5,7 @@ import sys from time import time from collections import OrderedDict -from typing import Any, Optional, Dict +from typing import Any, Optional, Dict, Set import voluptuous as vol @@ -391,14 +391,15 @@ async def async_mount_local_lib_path(config_dir: str) -> str: @core.callback -def _get_components(hass: core.HomeAssistant, config: Dict[str, Any]): +def _get_components(hass: core.HomeAssistant, + config: Dict[str, Any]) -> Set[str]: """Get components to set up.""" # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) # Add config entry domains - components.update(hass.config_entries.async_domains()) + components.update(hass.config_entries.async_domains()) # type: ignore # Make sure the Hass.io component is loaded if 'HASSIO' in os.environ: From 9d8054e6e2bc07e513a88c79dee2d135f3124894 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 20 Mar 2019 07:51:23 -0700 Subject: [PATCH 053/605] Bumped version to 0.90.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 8a405b9a7bd1aa..df0146cde62c72 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0b7' +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 01d8b5831ea6fd97dac1beea99844754c4520857 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 20 Mar 2019 07:45:09 -0700 Subject: [PATCH 054/605] Updated frontend to 20190320.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index ee7795894614da..6e05299ec52b6c 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190319.0'] +REQUIREMENTS = ['home-assistant-frontend==20190320.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 5be5bfeea3e823..06ad569822314f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -548,7 +548,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.0 +home-assistant-frontend==20190320.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 606aa3aecd717c..0795db63d52f02 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190319.0 +home-assistant-frontend==20190320.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 965354414439111200b4e2d0e1cd22b6c5f999b7 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 20 Mar 2019 22:15:21 -0400 Subject: [PATCH 055/605] Fix ZHA force polled entities. (#22222) ## Description: Fix "force_polled" ZHA entities. ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- homeassistant/components/zha/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index d0848222549340..1e98118e09f87c 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -179,7 +179,7 @@ def async_restore_last_state(self, last_state): async def async_update(self): """Retrieve latest state.""" - for channel in self.cluster_channels: + for channel in self.cluster_channels.values(): if hasattr(channel, 'async_update'): await channel.async_update() From e044eace20a4746e1141357c1c0c6f1a1bd14ca1 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 21 Mar 2019 04:30:52 +0100 Subject: [PATCH 056/605] Upgrade psutil to 5.6.1 (#22183) * Upgrade psutil to 5.6.1 * Upgrade speedtest-cli to 2.1.1 --- .../components/speedtestdotnet/__init__.py | 33 ++++++++----------- .../components/speedtestdotnet/sensor.py | 4 +-- .../components/systemmonitor/sensor.py | 2 +- requirements_all.txt | 4 +-- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index 4eae738b0d324f..0c8908a309f3de 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -1,21 +1,21 @@ """Support for testing internet speed via Speedtest.net.""" -import logging from datetime import timedelta +import logging import voluptuous as vol +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.speedtestdotnet.const import ( + DATA_UPDATED, DOMAIN, SENSOR_TYPES) +from homeassistant.const import ( + CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL, CONF_UPDATE_INTERVAL, + CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) import homeassistant.helpers.config_validation as cv -from homeassistant.components.speedtestdotnet.const import DOMAIN, \ - DATA_UPDATED, SENSOR_TYPES -from homeassistant.const import CONF_MONITORED_CONDITIONS, \ - CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, \ - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -REQUIREMENTS = ['speedtest-cli==2.0.2'] +REQUIREMENTS = ['speedtest-cli==2.1.1'] _LOGGER = logging.getLogger(__name__) @@ -34,8 +34,7 @@ vol.All(cv.time_period, cv.positive_timedelta), vol.Optional(CONF_MANUAL, default=False): cv.boolean, vol.Optional( - CONF_MONITORED_CONDITIONS, - default=list(SENSOR_TYPES) + CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES) ): vol.All(cv.ensure_list, [vol.In(list(SENSOR_TYPES))]) }), cv.deprecated( @@ -55,8 +54,7 @@ async def async_setup(hass, config): if not conf[CONF_MANUAL]: async_track_time_interval( - hass, data.update, conf[CONF_SCAN_INTERVAL] - ) + hass, data.update, conf[CONF_SCAN_INTERVAL]) def update(call=None): """Service call to manually update the data.""" @@ -66,13 +64,8 @@ def update(call=None): hass.async_create_task( async_load_platform( - hass, - SENSOR_DOMAIN, - DOMAIN, - conf[CONF_MONITORED_CONDITIONS], - config - ) - ) + hass, SENSOR_DOMAIN, DOMAIN, conf[CONF_MONITORED_CONDITIONS], + config)) return True @@ -89,7 +82,7 @@ def __init__(self, hass, server_id): def update(self, now=None): """Get the latest data from speedtest.net.""" import speedtest - _LOGGER.debug("Executing speedtest.net speedtest") + _LOGGER.debug("Executing speedtest.net speed test") speed = speedtest.Speedtest() speed.get_servers(self._servers) speed.get_best_server() diff --git a/homeassistant/components/speedtestdotnet/sensor.py b/homeassistant/components/speedtestdotnet/sensor.py index 4deb6550444f30..11bc9a37ca01f9 100644 --- a/homeassistant/components/speedtestdotnet/sensor.py +++ b/homeassistant/components/speedtestdotnet/sensor.py @@ -1,8 +1,8 @@ """Support for Speedtest.net internet speed testing sensor.""" import logging -from homeassistant.components.speedtestdotnet.const import \ - DOMAIN as SPEEDTESTDOTNET_DOMAIN, DATA_UPDATED, SENSOR_TYPES +from homeassistant.components.speedtestdotnet.const import ( + DATA_UPDATED, DOMAIN as SPEEDTESTDOTNET_DOMAIN, SENSOR_TYPES) from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect diff --git a/homeassistant/components/systemmonitor/sensor.py b/homeassistant/components/systemmonitor/sensor.py index 8eccdc7b3b7aaf..cf65daa439509c 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -12,7 +12,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['psutil==5.5.1'] +REQUIREMENTS = ['psutil==5.6.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 06ad569822314f..a61f39e2084c9e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -872,7 +872,7 @@ prometheus_client==0.2.0 protobuf==3.6.1 # homeassistant.components.systemmonitor.sensor -psutil==5.5.1 +psutil==5.6.1 # homeassistant.components.wink pubnubsub-handler==1.0.3 @@ -1619,7 +1619,7 @@ solaredge==0.0.2 somecomfort==0.5.2 # homeassistant.components.speedtestdotnet -speedtest-cli==2.0.2 +speedtest-cli==2.1.1 # homeassistant.components.spider spiderpy==1.3.1 From 423d595edff1c8457b54862e3e8b66f93bb4d4e4 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 21 Mar 2019 04:31:15 +0100 Subject: [PATCH 057/605] Upgrade holidays to 0.9.10 (#22182) --- .../components/workday/binary_sensor.py | 32 ++++++++++--------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py index 6b547927af4d38..b505e075018acc 100644 --- a/homeassistant/components/workday/binary_sensor.py +++ b/homeassistant/components/workday/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Sensor to indicate whether the current day is a workday. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.workday/ -""" +"""Sensor to indicate whether the current day is a workday.""" import logging from datetime import datetime, timedelta @@ -14,7 +9,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['holidays==0.9.9'] +REQUIREMENTS = ['holidays==0.9.10'] _LOGGER = logging.getLogger(__name__) @@ -22,15 +17,22 @@ # There seems to be no way to get the list out at runtime ALL_COUNTRIES = [ 'Argentina', 'AR', 'Australia', 'AU', 'Austria', 'AT', - 'Brazil', 'BR', 'Belarus', 'BY', 'Belgium', 'BE', + 'Brazil', 'BR', 'Belarus', 'BY', 'Belgium', 'BE', 'Bulgaria', 'BG', 'Canada', 'CA', 'Colombia', 'CO', 'Croatia', 'HR', 'Czech', 'CZ', - 'Denmark', 'DK', 'England', 'EuropeanCentralBank', 'ECB', 'TAR', - 'Finland', 'FI', 'France', 'FRA', 'Germany', 'DE', 'Hungary', 'HU', - 'Honduras', 'HUD', - 'India', 'IND', 'Ireland', 'Isle of Man', 'Italy', 'IT', 'Japan', 'JP', - 'Mexico', 'MX', 'Netherlands', 'NL', 'NewZealand', 'NZ', - 'Northern Ireland', 'Norway', 'NO', 'Polish', 'PL', 'Portugal', 'PT', - 'PortugalExt', 'PTE', 'Scotland', 'Slovenia', 'SI', 'Slovakia', 'SK', + 'Denmark', 'DK', + 'England', 'EuropeanCentralBank', 'ECB', 'TAR', + 'Finland', 'FI', 'France', 'FRA', + 'Germany', 'DE', + 'Hungary', 'HU', 'Honduras', 'HUD', + 'India', 'IND', 'Ireland', 'IE', 'Isle of Man', 'Italy', 'IT', + 'Japan', 'JP', + 'Lithuania', 'LT', 'Luxembourg', 'LU', + 'Mexico', 'MX', + 'Netherlands', 'NL', 'NewZealand', 'NZ', 'Northern Ireland', + 'Norway', 'NO', + 'Polish', 'PL', 'Portugal', 'PT', 'PortugalExt', 'PTE', + 'Russia', 'RU', + 'Scotland', 'Slovenia', 'SI', 'Slovakia', 'SK', 'South Africa', 'ZA', 'Spain', 'ES', 'Sweden', 'SE', 'Switzerland', 'CH', 'Ukraine', 'UA', 'UnitedKingdom', 'UK', 'UnitedStates', 'US', 'Wales', ] diff --git a/requirements_all.txt b/requirements_all.txt index a61f39e2084c9e..ef5aa8f8cfc016 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -545,7 +545,7 @@ hlk-sw16==0.0.7 hole==0.3.0 # homeassistant.components.workday.binary_sensor -holidays==0.9.9 +holidays==0.9.10 # homeassistant.components.frontend home-assistant-frontend==20190320.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0795db63d52f02..98ef1dbab43e6a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -123,7 +123,7 @@ hbmqtt==0.9.4 hdate==0.8.7 # homeassistant.components.workday.binary_sensor -holidays==0.9.9 +holidays==0.9.10 # homeassistant.components.frontend home-assistant-frontend==20190320.0 From ab17b22239452671f14067571f22aadb9688a3de Mon Sep 17 00:00:00 2001 From: Marco Orovecchia Date: Thu, 21 Mar 2019 04:32:06 +0100 Subject: [PATCH 058/605] Removed overly broad exception handling for nanoleaf light (#22189) --- homeassistant/components/nanoleaf/light.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/nanoleaf/light.py b/homeassistant/components/nanoleaf/light.py index bd34c535b70256..8cb899f1d285c1 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -195,6 +195,7 @@ def turn_off(self, **kwargs): def update(self): """Fetch new state data for this light.""" + from pynanoleaf import Unavailable try: self._available = self._light.available self._brightness = self._light.brightness @@ -203,7 +204,7 @@ def update(self): self._effects_list = self._light.effects self._hs_color = self._light.hue, self._light.saturation self._state = self._light.on - except Exception as err: # pylint:disable=broad-except + except Unavailable as err: _LOGGER.error("Could not update status for %s (%s)", self.name, err) self._available = False From 4b1de611103e148c9942e8f501400d6ee45720bb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 20 Mar 2019 22:56:46 -0700 Subject: [PATCH 059/605] Use relative imports inside integrations (#22235) * Use relative imports inside integrations * Lint * Fix automation tests * Fix scene imports --- .../components/abode/alarm_control_panel.py | 4 +-- .../components/abode/binary_sensor.py | 4 +-- homeassistant/components/abode/camera.py | 5 +-- homeassistant/components/abode/cover.py | 3 +- homeassistant/components/abode/light.py | 7 +++-- homeassistant/components/abode/lock.py | 3 +- homeassistant/components/abode/sensor.py | 3 +- homeassistant/components/abode/switch.py | 4 +-- homeassistant/components/ads/binary_sensor.py | 3 +- homeassistant/components/ads/light.py | 10 +++--- homeassistant/components/ads/sensor.py | 4 +-- homeassistant/components/ads/switch.py | 3 +- .../alarmdecoder/alarm_control_panel.py | 3 +- .../components/alarmdecoder/binary_sensor.py | 10 +++--- .../components/alarmdecoder/sensor.py | 3 +- .../ambient_station/binary_sensor.py | 8 ++--- .../components/ambient_station/sensor.py | 3 +- homeassistant/components/amcrest/camera.py | 7 ++--- homeassistant/components/amcrest/sensor.py | 5 +-- homeassistant/components/amcrest/switch.py | 6 ++-- .../android_ip_webcam/binary_sensor.py | 4 +-- .../components/android_ip_webcam/sensor.py | 7 +++-- .../components/android_ip_webcam/switch.py | 7 +++-- .../components/apple_tv/media_player.py | 4 +-- homeassistant/components/apple_tv/remote.py | 6 ++-- homeassistant/components/aqualogic/sensor.py | 13 ++++---- homeassistant/components/aqualogic/switch.py | 13 ++++---- .../components/arlo/alarm_control_panel.py | 14 ++++----- homeassistant/components/arlo/camera.py | 10 +++--- homeassistant/components/arlo/sensor.py | 13 ++++---- .../components/asterisk_mbox/mailbox.py | 3 +- .../components/asuswrt/device_tracker.py | 3 +- homeassistant/components/asuswrt/sensor.py | 3 +- .../components/august/binary_sensor.py | 7 +++-- homeassistant/components/august/camera.py | 3 +- homeassistant/components/august/lock.py | 5 +-- .../components/automation/__init__.py | 21 ++++++------- .../components/blink/alarm_control_panel.py | 6 ++-- .../components/blink/binary_sensor.py | 3 +- homeassistant/components/blink/camera.py | 3 +- homeassistant/components/blink/sensor.py | 5 +-- .../bmw_connected_drive/binary_sensor.py | 3 +- .../bmw_connected_drive/device_tracker.py | 4 +-- .../components/bmw_connected_drive/lock.py | 3 +- .../components/bmw_connected_drive/sensor.py | 9 +++--- homeassistant/components/bom/weather.py | 7 +++-- homeassistant/components/buienradar/sensor.py | 13 ++++---- .../components/buienradar/weather.py | 5 +-- homeassistant/components/calendar/demo.py | 5 +-- homeassistant/components/camera/demo.py | 2 +- .../components/canary/alarm_control_panel.py | 8 +++-- homeassistant/components/canary/camera.py | 7 +++-- homeassistant/components/canary/sensor.py | 3 +- homeassistant/components/cast/media_player.py | 13 ++++---- homeassistant/components/climate/demo.py | 20 ++++++------ .../components/climate/generic_thermostat.py | 22 ++++++------- homeassistant/components/comfoconnect/fan.py | 10 +++--- .../components/comfoconnect/sensor.py | 10 +++--- .../components/command_line/binary_sensor.py | 13 ++++---- homeassistant/components/config/automation.py | 5 +-- homeassistant/components/config/customize.py | 4 +-- homeassistant/components/config/group.py | 4 +-- homeassistant/components/config/script.py | 2 +- homeassistant/components/config/zwave.py | 3 +- .../components/conversation/__init__.py | 15 +++++---- homeassistant/components/cover/demo.py | 7 +++-- homeassistant/components/cover/group.py | 24 +++++++------- homeassistant/components/daikin/climate.py | 15 ++++----- homeassistant/components/daikin/sensor.py | 9 +++--- .../components/danfoss_air/binary_sensor.py | 4 +-- .../components/danfoss_air/sensor.py | 8 ++--- .../components/danfoss_air/switch.py | 7 ++--- homeassistant/components/deconz/const.py | 2 +- .../components/digital_ocean/binary_sensor.py | 13 ++++---- .../components/digital_ocean/switch.py | 13 ++++---- homeassistant/components/doorbird/camera.py | 3 +- homeassistant/components/doorbird/switch.py | 3 +- homeassistant/components/dovado/notify.py | 7 +++-- homeassistant/components/dovado/sensor.py | 7 +++-- homeassistant/components/dyson/climate.py | 9 +++--- homeassistant/components/dyson/fan.py | 5 +-- homeassistant/components/dyson/sensor.py | 3 +- homeassistant/components/dyson/vacuum.py | 3 +- .../components/ecoal_boiler/sensor.py | 4 +-- .../components/ecoal_boiler/switch.py | 4 +-- homeassistant/components/ecovacs/vacuum.py | 10 +++--- homeassistant/components/edp_redy/sensor.py | 4 +-- homeassistant/components/edp_redy/switch.py | 3 +- .../components/egardia/alarm_control_panel.py | 9 +++--- .../components/egardia/binary_sensor.py | 4 +-- .../components/eight_sleep/binary_sensor.py | 4 +-- .../components/eight_sleep/sensor.py | 6 ++-- .../components/elkm1/alarm_control_panel.py | 5 +-- homeassistant/components/elkm1/climate.py | 13 ++++---- homeassistant/components/elkm1/light.py | 4 +-- homeassistant/components/elkm1/scene.py | 4 +-- homeassistant/components/elkm1/sensor.py | 3 +- homeassistant/components/elkm1/switch.py | 4 +-- .../components/emulated_roku/binding.py | 2 +- .../envisalink/alarm_control_panel.py | 18 ++++++----- .../components/envisalink/binary_sensor.py | 13 ++++---- homeassistant/components/envisalink/sensor.py | 7 +++-- .../components/esphome/binary_sensor.py | 5 ++- homeassistant/components/esphome/cover.py | 9 +++--- homeassistant/components/esphome/fan.py | 11 ++++--- homeassistant/components/esphome/light.py | 18 +++++------ homeassistant/components/esphome/sensor.py | 6 ++-- homeassistant/components/esphome/switch.py | 5 ++- homeassistant/components/evohome/climate.py | 30 ++++++------------ homeassistant/components/fan/demo.py | 7 +++-- homeassistant/components/fastdotcom/sensor.py | 4 +-- homeassistant/components/ffmpeg/camera.py | 9 +++--- .../components/fibaro/binary_sensor.py | 8 ++--- homeassistant/components/fibaro/cover.py | 6 ++-- homeassistant/components/fibaro/light.py | 11 +++---- homeassistant/components/fibaro/scene.py | 7 ++--- homeassistant/components/fibaro/sensor.py | 10 +++--- homeassistant/components/fibaro/switch.py | 6 ++-- .../components/freebox/device_tracker.py | 3 +- homeassistant/components/freebox/sensor.py | 3 +- .../components/fritzbox/binary_sensor.py | 3 +- homeassistant/components/fritzbox/climate.py | 18 +++++------ homeassistant/components/fritzbox/sensor.py | 8 ++--- homeassistant/components/fritzbox/switch.py | 10 +++--- .../components/gc100/binary_sensor.py | 5 +-- homeassistant/components/gc100/switch.py | 7 +++-- homeassistant/components/geo_location/demo.py | 3 +- .../components/geofency/device_tracker.py | 4 +-- homeassistant/components/google/calendar.py | 10 +++--- .../components/googlehome/device_tracker.py | 6 ++-- homeassistant/components/googlehome/sensor.py | 6 ++-- .../components/gpslogger/device_tracker.py | 8 ++--- homeassistant/components/hangouts/__init__.py | 2 +- homeassistant/components/hangouts/const.py | 2 +- homeassistant/components/hangouts/notify.py | 5 +-- .../components/hdmi_cec/media_player.py | 3 +- homeassistant/components/hdmi_cec/switch.py | 3 +- .../components/hive/binary_sensor.py | 3 +- homeassistant/components/hive/climate.py | 7 +++-- homeassistant/components/hive/light.py | 3 +- homeassistant/components/hive/sensor.py | 3 +- homeassistant/components/hive/switch.py | 3 +- homeassistant/components/hlk_sw16/switch.py | 8 ++--- .../homekit_controller/alarm_control_panel.py | 4 +-- .../homekit_controller/binary_sensor.py | 4 +-- .../components/homekit_controller/climate.py | 10 +++--- .../components/homekit_controller/cover.py | 4 +-- .../components/homekit_controller/light.py | 4 +-- .../components/homekit_controller/lock.py | 4 +-- .../components/homekit_controller/sensor.py | 4 +-- .../components/homekit_controller/switch.py | 4 +-- .../components/homematic/binary_sensor.py | 3 +- homeassistant/components/homematic/climate.py | 4 +-- homeassistant/components/homematic/cover.py | 3 +- homeassistant/components/homematic/light.py | 3 +- homeassistant/components/homematic/lock.py | 3 +- homeassistant/components/homematic/notify.py | 7 +++-- homeassistant/components/homematic/sensor.py | 5 +-- homeassistant/components/homematic/switch.py | 3 +- .../homematicip_cloud/alarm_control_panel.py | 4 +-- .../homematicip_cloud/binary_sensor.py | 7 ++--- .../components/homematicip_cloud/climate.py | 4 +-- .../components/homematicip_cloud/const.py | 2 +- .../components/homematicip_cloud/cover.py | 4 +-- .../components/homematicip_cloud/light.py | 4 +-- .../components/homematicip_cloud/sensor.py | 4 +-- .../components/homematicip_cloud/switch.py | 7 ++--- .../components/homematicip_cloud/weather.py | 4 +-- homeassistant/components/homeworks/light.py | 7 +++-- homeassistant/components/http/view.py | 4 +-- homeassistant/components/hue/const.py | 2 +- .../components/hydrawise/binary_sensor.py | 11 ++++--- homeassistant/components/hydrawise/sensor.py | 7 +++-- homeassistant/components/hydrawise/switch.py | 11 ++++--- .../components/ifttt/alarm_control_panel.py | 4 +-- homeassistant/components/ihc/__init__.py | 15 ++++----- homeassistant/components/ihc/binary_sensor.py | 7 +++-- homeassistant/components/ihc/light.py | 11 +++---- homeassistant/components/ihc/sensor.py | 5 +-- homeassistant/components/ihc/switch.py | 9 +++--- homeassistant/components/influxdb/sensor.py | 3 +- .../components/insteon/binary_sensor.py | 3 +- homeassistant/components/insteon/cover.py | 3 +- homeassistant/components/insteon/fan.py | 3 +- homeassistant/components/insteon/light.py | 3 +- homeassistant/components/insteon/sensor.py | 3 +- homeassistant/components/insteon/switch.py | 3 +- homeassistant/components/iota/sensor.py | 5 +-- homeassistant/components/iperf3/sensor.py | 4 +-- homeassistant/components/ipma/const.py | 2 +- .../components/isy994/binary_sensor.py | 4 +-- homeassistant/components/isy994/cover.py | 4 +-- homeassistant/components/isy994/fan.py | 4 +-- homeassistant/components/isy994/light.py | 3 +- homeassistant/components/isy994/lock.py | 4 +-- homeassistant/components/isy994/sensor.py | 6 ++-- homeassistant/components/isy994/switch.py | 4 +-- homeassistant/components/juicenet/sensor.py | 5 +-- homeassistant/components/knx/binary_sensor.py | 4 +-- homeassistant/components/knx/climate.py | 3 +- homeassistant/components/knx/cover.py | 3 +- homeassistant/components/knx/light.py | 3 +- homeassistant/components/knx/notify.py | 3 +- homeassistant/components/knx/scene.py | 3 +- homeassistant/components/knx/sensor.py | 3 +- homeassistant/components/knx/switch.py | 3 +- .../components/konnected/binary_sensor.py | 8 ++--- homeassistant/components/konnected/sensor.py | 7 +++-- homeassistant/components/konnected/switch.py | 9 +++--- homeassistant/components/lametric/notify.py | 4 +-- homeassistant/components/lcn/__init__.py | 13 ++++---- homeassistant/components/lcn/cover.py | 6 ++-- homeassistant/components/lcn/light.py | 9 +++--- homeassistant/components/lcn/sensor.py | 7 +++-- homeassistant/components/lcn/switch.py | 6 ++-- homeassistant/components/lifx/light.py | 7 +++-- homeassistant/components/light/demo.py | 6 ++-- homeassistant/components/light/group.py | 31 ++++++++++--------- homeassistant/components/light/switch.py | 18 +++++------ homeassistant/components/lightwave/light.py | 3 +- homeassistant/components/lightwave/switch.py | 3 +- .../components/linode/binary_sensor.py | 5 +-- homeassistant/components/linode/switch.py | 7 +++-- .../components/locative/device_tracker.py | 8 ++--- homeassistant/components/lock/demo.py | 5 +-- .../components/logi_circle/camera.py | 14 ++++----- .../components/logi_circle/sensor.py | 12 +++---- homeassistant/components/luftdaten/sensor.py | 9 +++--- .../components/lupusec/alarm_control_panel.py | 11 +++---- .../components/lupusec/binary_sensor.py | 10 +++--- homeassistant/components/lupusec/switch.py | 6 ++-- homeassistant/components/lutron/cover.py | 8 ++--- homeassistant/components/lutron/light.py | 4 +-- homeassistant/components/lutron/scene.py | 4 +-- homeassistant/components/lutron/switch.py | 4 +-- .../components/lutron_caseta/cover.py | 8 ++--- .../components/lutron_caseta/light.py | 6 ++-- .../components/lutron_caseta/scene.py | 3 +- .../components/lutron_caseta/switch.py | 6 ++-- homeassistant/components/mailgun/notify.py | 8 ++--- .../components/maxcube/binary_sensor.py | 3 +- homeassistant/components/maxcube/climate.py | 10 +++--- homeassistant/components/media_player/demo.py | 8 ++--- homeassistant/components/melissa/climate.py | 17 +++++----- .../components/meteo_france/sensor.py | 4 +-- .../components/meteo_france/weather.py | 4 +-- homeassistant/components/metoffice/weather.py | 4 +-- .../components/modbus/binary_sensor.py | 4 +-- homeassistant/components/modbus/climate.py | 4 +-- homeassistant/components/modbus/sensor.py | 4 +-- homeassistant/components/modbus/switch.py | 4 +-- homeassistant/components/mqtt/__init__.py | 6 ++-- .../components/mqtt/alarm_control_panel.py | 12 +++---- .../components/mqtt/binary_sensor.py | 12 +++---- homeassistant/components/mqtt/camera.py | 10 +++--- homeassistant/components/mqtt/climate.py | 23 +++++++------- homeassistant/components/mqtt/cover.py | 12 +++---- .../components/mqtt/device_tracker.py | 7 +++-- homeassistant/components/mqtt/discovery.py | 3 +- homeassistant/components/mqtt/fan.py | 12 +++---- homeassistant/components/mqtt/lock.py | 12 +++---- homeassistant/components/mqtt/sensor.py | 12 +++---- homeassistant/components/mqtt/subscription.py | 3 +- homeassistant/components/mqtt/switch.py | 12 +++---- homeassistant/components/mqtt/vacuum.py | 10 +++--- .../components/mychevy/binary_sensor.py | 5 ++- homeassistant/components/mychevy/sensor.py | 8 ++--- homeassistant/components/neato/camera.py | 6 ++-- homeassistant/components/neato/switch.py | 7 +++-- homeassistant/components/neato/vacuum.py | 25 ++++++++------- .../ness_alarm/alarm_control_panel.py | 8 ++--- .../components/ness_alarm/binary_sensor.py | 7 +++-- .../components/nest/binary_sensor.py | 5 +-- homeassistant/components/nest/climate.py | 19 ++++++------ homeassistant/components/nest/sensor.py | 11 +++---- .../components/netatmo/binary_sensor.py | 5 +-- homeassistant/components/netatmo/camera.py | 5 +-- .../components/nissan_leaf/binary_sensor.py | 4 +-- .../components/nissan_leaf/device_tracker.py | 4 +-- .../components/nissan_leaf/sensor.py | 7 +++-- .../components/nissan_leaf/switch.py | 4 +-- homeassistant/components/notify/apns.py | 11 ++++--- homeassistant/components/notify/aws_lambda.py | 11 +++---- homeassistant/components/notify/aws_sns.py | 12 +++---- homeassistant/components/notify/aws_sqs.py | 9 +++--- homeassistant/components/notify/ciscospark.py | 6 ++-- homeassistant/components/notify/clickatell.py | 6 ++-- homeassistant/components/notify/clicksend.py | 8 ++--- .../components/notify/clicksend_tts.py | 10 +++--- .../components/notify/command_line.py | 6 ++-- homeassistant/components/notify/demo.py | 2 +- homeassistant/components/notify/discord.py | 6 ++-- homeassistant/components/notify/facebook.py | 4 +-- homeassistant/components/notify/file.py | 7 +++-- homeassistant/components/notify/flock.py | 4 +-- .../components/notify/free_mobile.py | 4 +-- homeassistant/components/notify/gntp.py | 5 +-- homeassistant/components/notify/group.py | 6 ++-- homeassistant/components/notify/hipchat.py | 6 ++-- homeassistant/components/notify/html5.py | 15 ++++----- homeassistant/components/notify/kodi.py | 11 ++++--- homeassistant/components/notify/lannouncer.py | 10 +++--- .../components/notify/llamalab_automate.py | 4 +-- homeassistant/components/notify/mastodon.py | 4 +-- .../components/notify/message_bird.py | 6 ++-- homeassistant/components/notify/mycroft.py | 3 +- .../components/notify/nfandroidtv.py | 14 ++++----- homeassistant/components/notify/prowl.py | 11 ++++--- homeassistant/components/notify/pushbullet.py | 7 +++-- homeassistant/components/notify/pushetta.py | 5 +-- homeassistant/components/notify/pushover.py | 7 +++-- homeassistant/components/notify/pushsafer.py | 9 +++--- homeassistant/components/notify/rest.py | 16 +++++----- homeassistant/components/notify/rocketchat.py | 8 ++--- homeassistant/components/notify/sendgrid.py | 7 +++-- homeassistant/components/notify/simplepush.py | 5 +-- homeassistant/components/notify/slack.py | 10 +++--- homeassistant/components/notify/smtp.py | 19 ++++++------ homeassistant/components/notify/stride.py | 6 ++-- .../components/notify/synology_chat.py | 6 ++-- homeassistant/components/notify/syslog.py | 2 +- homeassistant/components/notify/telegram.py | 7 +++-- .../components/notify/twilio_call.py | 4 +-- homeassistant/components/notify/twilio_sms.py | 4 +-- homeassistant/components/notify/twitter.py | 10 +++--- homeassistant/components/notify/xmpp.py | 5 +-- homeassistant/components/notify/yessssms.py | 5 ++- homeassistant/components/nuheat/climate.py | 19 ++++-------- .../components/octoprint/binary_sensor.py | 4 +-- homeassistant/components/octoprint/sensor.py | 6 ++-- .../components/opentherm_gw/binary_sensor.py | 6 ++-- .../components/opentherm_gw/climate.py | 13 ++++---- .../components/opentherm_gw/sensor.py | 4 +-- .../components/openuv/binary_sensor.py | 7 +++-- homeassistant/components/openuv/sensor.py | 9 +++--- .../components/owlet/binary_sensor.py | 2 +- homeassistant/components/owlet/sensor.py | 2 +- .../components/owntracks/device_tracker.py | 7 ++--- .../components/plum_lightpad/light.py | 3 +- .../components/point/alarm_control_panel.py | 4 +-- .../components/point/binary_sensor.py | 6 ++-- homeassistant/components/point/sensor.py | 6 ++-- homeassistant/components/ps4/__init__.py | 5 +-- homeassistant/components/ps4/config_flow.py | 4 +-- homeassistant/components/ps4/media_player.py | 16 +++++----- .../components/qwikswitch/binary_sensor.py | 3 +- homeassistant/components/qwikswitch/light.py | 4 +-- homeassistant/components/qwikswitch/sensor.py | 3 +- homeassistant/components/qwikswitch/switch.py | 4 +-- .../components/rachio/binary_sensor.py | 14 +++------ homeassistant/components/rachio/switch.py | 25 +++++---------- homeassistant/components/rainbird/sensor.py | 7 +++-- homeassistant/components/rainbird/switch.py | 11 ++++--- .../components/raincloud/binary_sensor.py | 8 ++--- homeassistant/components/raincloud/sensor.py | 6 ++-- homeassistant/components/raincloud/switch.py | 12 +++---- .../components/rainmachine/binary_sensor.py | 7 +++-- homeassistant/components/rainmachine/const.py | 4 +-- .../components/rainmachine/sensor.py | 7 +++-- .../components/rainmachine/switch.py | 11 ++++--- .../components/raspihats/binary_sensor.py | 7 +++-- homeassistant/components/raspihats/switch.py | 7 +++-- .../components/rest/binary_sensor.py | 17 +++++----- .../components/rflink/binary_sensor.py | 7 ++--- homeassistant/components/rflink/cover.py | 12 +++---- homeassistant/components/rflink/light.py | 10 +++--- homeassistant/components/rflink/sensor.py | 18 +++++------ homeassistant/components/rflink/switch.py | 10 +++--- .../components/rfxtrx/binary_sensor.py | 13 ++++---- homeassistant/components/rfxtrx/cover.py | 9 +++--- homeassistant/components/rfxtrx/light.py | 9 +++--- homeassistant/components/rfxtrx/sensor.py | 7 +++-- homeassistant/components/rfxtrx/switch.py | 11 ++++--- .../components/ring/binary_sensor.py | 12 +++---- homeassistant/components/ring/camera.py | 11 +++---- homeassistant/components/ring/sensor.py | 9 +++--- homeassistant/components/sabnzbd/sensor.py | 4 +-- homeassistant/components/scene/__init__.py | 8 ++--- .../components/scene/homeassistant.py | 7 +++-- .../components/sense/binary_sensor.py | 3 +- homeassistant/components/sense/sensor.py | 5 +-- .../simplisafe/alarm_control_panel.py | 4 +-- homeassistant/components/sisyphus/light.py | 5 +-- .../components/sisyphus/media_player.py | 3 +- .../components/skybell/binary_sensor.py | 6 ++-- homeassistant/components/skybell/camera.py | 6 ++-- homeassistant/components/skybell/light.py | 8 ++--- homeassistant/components/skybell/sensor.py | 4 +-- homeassistant/components/skybell/switch.py | 5 ++- homeassistant/components/smappee/sensor.py | 7 +++-- homeassistant/components/smappee/switch.py | 5 +-- homeassistant/components/smhi/const.py | 2 +- homeassistant/components/smhi/weather.py | 4 +-- .../components/sonos/media_player.py | 16 +++++----- .../components/spc/alarm_control_panel.py | 7 +++-- homeassistant/components/spc/binary_sensor.py | 5 +-- .../components/speedtestdotnet/__init__.py | 4 +-- .../components/speedtestdotnet/sensor.py | 5 +-- homeassistant/components/spider/climate.py | 8 ++--- homeassistant/components/spider/switch.py | 3 +- homeassistant/components/tado/climate.py | 9 +++--- homeassistant/components/tado/sensor.py | 3 +- .../components/tahoma/binary_sensor.py | 11 +++---- homeassistant/components/tahoma/cover.py | 6 ++-- homeassistant/components/tahoma/scene.py | 4 +-- homeassistant/components/tahoma/sensor.py | 8 ++--- homeassistant/components/tahoma/switch.py | 6 ++-- homeassistant/components/tcp/binary_sensor.py | 4 +-- .../components/telegram_bot/broadcast.py | 4 +-- .../components/telegram_bot/polling.py | 8 ++--- .../components/telegram_bot/webhooks.py | 10 +++--- .../components/tellduslive/binary_sensor.py | 3 +- homeassistant/components/tellduslive/cover.py | 3 +- homeassistant/components/tellduslive/light.py | 3 +- .../components/tellduslive/sensor.py | 5 +-- .../components/tellduslive/switch.py | 3 +- homeassistant/components/tellstick/cover.py | 7 +++-- homeassistant/components/tellstick/light.py | 6 ++-- homeassistant/components/tellstick/switch.py | 7 +++-- .../components/tesla/binary_sensor.py | 5 +-- homeassistant/components/tesla/climate.py | 6 ++-- .../components/tesla/device_tracker.py | 3 +- homeassistant/components/tesla/lock.py | 4 +-- homeassistant/components/tesla/sensor.py | 6 ++-- homeassistant/components/tesla/switch.py | 4 +-- .../components/thethingsnetwork/sensor.py | 4 +-- homeassistant/components/tibber/notify.py | 3 +- homeassistant/components/tibber/sensor.py | 9 +++--- homeassistant/components/tplink/light.py | 15 +++++---- homeassistant/components/tplink/switch.py | 6 ++-- homeassistant/components/tradfri/light.py | 15 ++++----- homeassistant/components/tradfri/sensor.py | 6 ++-- homeassistant/components/tradfri/switch.py | 9 +++--- .../components/transmission/sensor.py | 5 ++- .../components/transmission/switch.py | 7 ++--- homeassistant/components/tts/amazon_polly.py | 3 +- homeassistant/components/tts/baidu.py | 4 ++- homeassistant/components/tts/demo.py | 2 +- homeassistant/components/tts/marytts.py | 3 +- homeassistant/components/tts/microsoft.py | 7 +++-- homeassistant/components/tts/picotts.py | 7 +++-- homeassistant/components/tts/voicerss.py | 3 +- homeassistant/components/tts/yandextts.py | 2 +- homeassistant/components/tuya/climate.py | 14 ++++----- homeassistant/components/tuya/cover.py | 5 +-- homeassistant/components/tuya/fan.py | 5 +-- homeassistant/components/tuya/light.py | 6 ++-- homeassistant/components/tuya/scene.py | 5 +-- homeassistant/components/tuya/switch.py | 3 +- homeassistant/components/unifi/const.py | 2 +- homeassistant/components/unifi/switch.py | 7 ++--- .../components/upcloud/binary_sensor.py | 8 ++--- homeassistant/components/upcloud/switch.py | 6 ++-- homeassistant/components/upnp/const.py | 3 +- homeassistant/components/upnp/sensor.py | 3 +- homeassistant/components/usps/camera.py | 3 +- homeassistant/components/usps/sensor.py | 3 +- homeassistant/components/vacuum/demo.py | 13 ++++---- .../components/velbus/binary_sensor.py | 4 +-- homeassistant/components/velbus/climate.py | 7 ++--- homeassistant/components/velbus/cover.py | 8 ++--- homeassistant/components/velbus/sensor.py | 3 +- homeassistant/components/velbus/switch.py | 4 +-- homeassistant/components/velux/cover.py | 3 +- homeassistant/components/velux/scene.py | 3 +- .../components/vera/binary_sensor.py | 6 ++-- homeassistant/components/vera/climate.py | 20 +++++------- homeassistant/components/vera/cover.py | 8 ++--- homeassistant/components/vera/light.py | 8 ++--- homeassistant/components/vera/lock.py | 6 ++-- homeassistant/components/vera/scene.py | 6 ++-- homeassistant/components/vera/sensor.py | 11 +++---- homeassistant/components/vera/switch.py | 6 ++-- .../verisure/alarm_control_panel.py | 4 +-- .../components/verisure/binary_sensor.py | 4 +-- homeassistant/components/verisure/camera.py | 4 +-- homeassistant/components/verisure/lock.py | 12 +++---- homeassistant/components/verisure/sensor.py | 5 ++- homeassistant/components/verisure/switch.py | 4 +-- .../components/volvooncall/binary_sensor.py | 5 +-- .../components/volvooncall/device_tracker.py | 7 +++-- homeassistant/components/volvooncall/lock.py | 3 +- .../components/volvooncall/sensor.py | 2 +- .../components/volvooncall/switch.py | 3 +- .../components/vultr/binary_sensor.py | 17 +++++----- homeassistant/components/vultr/sensor.py | 7 +++-- homeassistant/components/vultr/switch.py | 15 ++++----- .../components/w800rf32/binary_sensor.py | 10 +++--- homeassistant/components/water_heater/demo.py | 11 +++---- .../components/waterfurnace/sensor.py | 5 ++- .../components/wink/alarm_control_panel.py | 3 +- .../components/wink/binary_sensor.py | 3 +- homeassistant/components/wink/climate.py | 8 ++--- homeassistant/components/wink/cover.py | 5 +-- homeassistant/components/wink/fan.py | 7 +++-- homeassistant/components/wink/light.py | 9 +++--- homeassistant/components/wink/lock.py | 3 +- homeassistant/components/wink/scene.py | 3 +- homeassistant/components/wink/sensor.py | 3 +- homeassistant/components/wink/switch.py | 3 +- homeassistant/components/wink/water_heater.py | 14 ++++----- .../components/wirelesstag/binary_sensor.py | 15 +++++---- .../components/wirelesstag/sensor.py | 13 ++++---- .../components/wirelesstag/switch.py | 9 ++---- .../components/xiaomi_aqara/binary_sensor.py | 4 +-- .../components/xiaomi_aqara/cover.py | 6 ++-- .../components/xiaomi_aqara/light.py | 12 +++---- homeassistant/components/xiaomi_aqara/lock.py | 9 +++--- .../components/xiaomi_aqara/sensor.py | 8 ++--- .../components/xiaomi_aqara/switch.py | 4 +-- homeassistant/components/xs1/climate.py | 7 ++--- homeassistant/components/xs1/sensor.py | 4 +-- homeassistant/components/xs1/switch.py | 4 +-- homeassistant/components/zamg/weather.py | 7 +++-- .../components/zigbee/binary_sensor.py | 4 +-- homeassistant/components/zigbee/light.py | 4 +-- homeassistant/components/zigbee/sensor.py | 5 +-- homeassistant/components/zigbee/switch.py | 4 +-- .../components/zoneminder/binary_sensor.py | 2 +- homeassistant/components/zoneminder/camera.py | 5 +-- homeassistant/components/zoneminder/sensor.py | 5 +-- homeassistant/components/zoneminder/switch.py | 7 +++-- 522 files changed, 1830 insertions(+), 1721 deletions(-) diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index 838d09b73af3ef..d7426e04166dc5 100644 --- a/homeassistant/components/abode/alarm_control_panel.py +++ b/homeassistant/components/abode/alarm_control_panel.py @@ -2,12 +2,12 @@ import logging import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.abode import ATTRIBUTION, AbodeDevice -from homeassistant.components.abode import DOMAIN as ABODE_DOMAIN from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) +from . import ATTRIBUTION, DOMAIN as ABODE_DOMAIN, AbodeDevice + DEPENDENCIES = ['abode'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/binary_sensor.py b/homeassistant/components/abode/binary_sensor.py index 47baef1d7e5d70..874723420ed811 100644 --- a/homeassistant/components/abode/binary_sensor.py +++ b/homeassistant/components/abode/binary_sensor.py @@ -1,10 +1,10 @@ """Support for Abode Security System binary sensors.""" import logging -from homeassistant.components.abode import (AbodeDevice, AbodeAutomation, - DOMAIN as ABODE_DOMAIN) from homeassistant.components.binary_sensor import BinarySensorDevice +from . import DOMAIN as ABODE_DOMAIN, AbodeAutomation, AbodeDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['abode'] diff --git a/homeassistant/components/abode/camera.py b/homeassistant/components/abode/camera.py index 99613d07c47e8d..d37644eccc397e 100644 --- a/homeassistant/components/abode/camera.py +++ b/homeassistant/components/abode/camera.py @@ -1,13 +1,14 @@ """Support for Abode Security System cameras.""" +from datetime import timedelta import logging -from datetime import timedelta import requests -from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.components.camera import Camera from homeassistant.util import Throttle +from . import DOMAIN as ABODE_DOMAIN, AbodeDevice + DEPENDENCIES = ['abode'] MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) diff --git a/homeassistant/components/abode/cover.py b/homeassistant/components/abode/cover.py index 03d6219ebce4b2..c40159164dc4db 100644 --- a/homeassistant/components/abode/cover.py +++ b/homeassistant/components/abode/cover.py @@ -1,9 +1,10 @@ """Support for Abode Security System covers.""" import logging -from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.components.cover import CoverDevice +from . import DOMAIN as ABODE_DOMAIN, AbodeDevice + DEPENDENCIES = ['abode'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/light.py b/homeassistant/components/abode/light.py index aabf5fbccdc238..9e88acce41f053 100644 --- a/homeassistant/components/abode/light.py +++ b/homeassistant/components/abode/light.py @@ -1,13 +1,14 @@ """Support for Abode Security System lights.""" import logging from math import ceil -from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN + from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_COLOR_TEMP, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) from homeassistant.util.color import ( color_temperature_kelvin_to_mired, color_temperature_mired_to_kelvin) +from . import DOMAIN as ABODE_DOMAIN, AbodeDevice DEPENDENCIES = ['abode'] diff --git a/homeassistant/components/abode/lock.py b/homeassistant/components/abode/lock.py index ce6634268e96e2..0f568a4ace2492 100644 --- a/homeassistant/components/abode/lock.py +++ b/homeassistant/components/abode/lock.py @@ -1,9 +1,10 @@ """Support for Abode Security System locks.""" import logging -from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.components.lock import LockDevice +from . import DOMAIN as ABODE_DOMAIN, AbodeDevice + DEPENDENCIES = ['abode'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/sensor.py b/homeassistant/components/abode/sensor.py index fa6cb9323bf28b..ef6941c76d8b5f 100644 --- a/homeassistant/components/abode/sensor.py +++ b/homeassistant/components/abode/sensor.py @@ -1,10 +1,11 @@ """Support for Abode Security System sensors.""" import logging -from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.const import ( DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE) +from . import DOMAIN as ABODE_DOMAIN, AbodeDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['abode'] diff --git a/homeassistant/components/abode/switch.py b/homeassistant/components/abode/switch.py index d5303a27cd2fb9..3e3ce031855fd0 100644 --- a/homeassistant/components/abode/switch.py +++ b/homeassistant/components/abode/switch.py @@ -1,10 +1,10 @@ """Support for Abode Security System switches.""" import logging -from homeassistant.components.abode import (AbodeDevice, AbodeAutomation, - DOMAIN as ABODE_DOMAIN) from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as ABODE_DOMAIN, AbodeAutomation, AbodeDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['abode'] diff --git a/homeassistant/components/ads/binary_sensor.py b/homeassistant/components/ads/binary_sensor.py index 6771e99cd77d17..3b935156d1861f 100644 --- a/homeassistant/components/ads/binary_sensor.py +++ b/homeassistant/components/ads/binary_sensor.py @@ -3,12 +3,13 @@ import voluptuous as vol -from homeassistant.components.ads import CONF_ADS_VAR, DATA_ADS from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME import homeassistant.helpers.config_validation as cv +from . import CONF_ADS_VAR, DATA_ADS + _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'ADS binary sensor' diff --git a/homeassistant/components/ads/light.py b/homeassistant/components/ads/light.py index e5299821e39c91..c61fd813634027 100644 --- a/homeassistant/components/ads/light.py +++ b/homeassistant/components/ads/light.py @@ -1,13 +1,15 @@ """Support for ADS light sources.""" import logging + import voluptuous as vol -from homeassistant.components.light import Light, ATTR_BRIGHTNESS, \ - SUPPORT_BRIGHTNESS, PLATFORM_SCHEMA + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light) from homeassistant.const import CONF_NAME -from homeassistant.components.ads import DATA_ADS, CONF_ADS_VAR, \ - CONF_ADS_VAR_BRIGHTNESS import homeassistant.helpers.config_validation as cv +from . import CONF_ADS_VAR, CONF_ADS_VAR_BRIGHTNESS, DATA_ADS + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ads'] DEFAULT_NAME = 'ADS Light' diff --git a/homeassistant/components/ads/sensor.py b/homeassistant/components/ads/sensor.py index 4db6ca7dbca2ac..118a669a7ad4a1 100644 --- a/homeassistant/components/ads/sensor.py +++ b/homeassistant/components/ads/sensor.py @@ -4,13 +4,13 @@ import voluptuous as vol from homeassistant.components import ads -from homeassistant.components.ads import ( - CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR + _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "ADS sensor" diff --git a/homeassistant/components/ads/switch.py b/homeassistant/components/ads/switch.py index e3aee023f21f21..094b455234956b 100644 --- a/homeassistant/components/ads/switch.py +++ b/homeassistant/components/ads/switch.py @@ -3,12 +3,13 @@ import voluptuous as vol -from homeassistant.components.ads import CONF_ADS_VAR, DATA_ADS from homeassistant.components.switch import PLATFORM_SCHEMA from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import ToggleEntity +from . import CONF_ADS_VAR, DATA_ADS + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ads'] diff --git a/homeassistant/components/alarmdecoder/alarm_control_panel.py b/homeassistant/components/alarmdecoder/alarm_control_panel.py index cf26e42b05684b..d7eced933ddbd1 100644 --- a/homeassistant/components/alarmdecoder/alarm_control_panel.py +++ b/homeassistant/components/alarmdecoder/alarm_control_panel.py @@ -4,12 +4,13 @@ import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.alarmdecoder import DATA_AD, SIGNAL_PANEL_MESSAGE from homeassistant.const import ( ATTR_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) import homeassistant.helpers.config_validation as cv +from . import DATA_AD, SIGNAL_PANEL_MESSAGE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['alarmdecoder'] diff --git a/homeassistant/components/alarmdecoder/binary_sensor.py b/homeassistant/components/alarmdecoder/binary_sensor.py index c5af6ea79cb7ce..09e63b4d664348 100644 --- a/homeassistant/components/alarmdecoder/binary_sensor.py +++ b/homeassistant/components/alarmdecoder/binary_sensor.py @@ -2,11 +2,11 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.alarmdecoder import ( - ZONE_SCHEMA, CONF_ZONES, CONF_ZONE_NAME, CONF_ZONE_TYPE, - CONF_ZONE_RFID, CONF_ZONE_LOOP, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE, - SIGNAL_RFX_MESSAGE, SIGNAL_REL_MESSAGE, CONF_RELAY_ADDR, - CONF_RELAY_CHAN) + +from . import ( + CONF_RELAY_ADDR, CONF_RELAY_CHAN, CONF_ZONE_LOOP, CONF_ZONE_NAME, + CONF_ZONE_RFID, CONF_ZONE_TYPE, CONF_ZONES, SIGNAL_REL_MESSAGE, + SIGNAL_RFX_MESSAGE, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE, ZONE_SCHEMA) DEPENDENCIES = ['alarmdecoder'] diff --git a/homeassistant/components/alarmdecoder/sensor.py b/homeassistant/components/alarmdecoder/sensor.py index b2f697ea83f226..88371dad17a345 100644 --- a/homeassistant/components/alarmdecoder/sensor.py +++ b/homeassistant/components/alarmdecoder/sensor.py @@ -2,7 +2,8 @@ import logging from homeassistant.helpers.entity import Entity -from homeassistant.components.alarmdecoder import (SIGNAL_PANEL_MESSAGE) + +from . import SIGNAL_PANEL_MESSAGE _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ambient_station/binary_sensor.py b/homeassistant/components/ambient_station/binary_sensor.py index 2defa032809a1b..04a38901683d94 100644 --- a/homeassistant/components/ambient_station/binary_sensor.py +++ b/homeassistant/components/ambient_station/binary_sensor.py @@ -1,13 +1,13 @@ """Support for Ambient Weather Station binary sensors.""" import logging -from homeassistant.components.ambient_station import ( - SENSOR_TYPES, TYPE_BATT1, TYPE_BATT10, TYPE_BATT2, TYPE_BATT3, TYPE_BATT4, - TYPE_BATT5, TYPE_BATT6, TYPE_BATT7, TYPE_BATT8, TYPE_BATT9, TYPE_BATTOUT, - AmbientWeatherEntity) from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.const import ATTR_NAME +from . import ( + SENSOR_TYPES, TYPE_BATT1, TYPE_BATT2, TYPE_BATT3, TYPE_BATT4, TYPE_BATT5, + TYPE_BATT6, TYPE_BATT7, TYPE_BATT8, TYPE_BATT9, TYPE_BATT10, TYPE_BATTOUT, + AmbientWeatherEntity) from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_BINARY_SENSOR _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ambient_station/sensor.py b/homeassistant/components/ambient_station/sensor.py index fa3222bf0e44eb..b394dc558e63c6 100644 --- a/homeassistant/components/ambient_station/sensor.py +++ b/homeassistant/components/ambient_station/sensor.py @@ -1,10 +1,9 @@ """Support for Ambient Weather Station sensors.""" import logging -from homeassistant.components.ambient_station import ( - SENSOR_TYPES, AmbientWeatherEntity) from homeassistant.const import ATTR_NAME +from . import SENSOR_TYPES, AmbientWeatherEntity from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_SENSOR _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 32885c2a83c09d..7ba3ea04bf5563 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -2,15 +2,14 @@ import asyncio import logging -from homeassistant.components.amcrest import ( - DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT) from homeassistant.components.camera import Camera from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import ( - async_get_clientsession, async_aiohttp_proxy_web, - async_aiohttp_proxy_stream) + async_aiohttp_proxy_stream, async_aiohttp_proxy_web, + async_get_clientsession) +from . import DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT DEPENDENCIES = ['amcrest', 'ffmpeg'] diff --git a/homeassistant/components/amcrest/sensor.py b/homeassistant/components/amcrest/sensor.py index 4869dfffa6e5bc..c721914c73cf81 100644 --- a/homeassistant/components/amcrest/sensor.py +++ b/homeassistant/components/amcrest/sensor.py @@ -2,9 +2,10 @@ from datetime import timedelta import logging -from homeassistant.components.amcrest import DATA_AMCREST, SENSORS -from homeassistant.helpers.entity import Entity from homeassistant.const import CONF_NAME, CONF_SENSORS +from homeassistant.helpers.entity import Entity + +from . import DATA_AMCREST, SENSORS DEPENDENCIES = ['amcrest'] diff --git a/homeassistant/components/amcrest/switch.py b/homeassistant/components/amcrest/switch.py index 3c1f03f01452aa..0bbd290b3ac253 100644 --- a/homeassistant/components/amcrest/switch.py +++ b/homeassistant/components/amcrest/switch.py @@ -1,11 +1,11 @@ """Support for toggling Amcrest IP camera settings.""" import logging -from homeassistant.components.amcrest import DATA_AMCREST, SWITCHES -from homeassistant.const import ( - CONF_NAME, CONF_SWITCHES, STATE_OFF, STATE_ON) +from homeassistant.const import CONF_NAME, CONF_SWITCHES, STATE_OFF, STATE_ON from homeassistant.helpers.entity import ToggleEntity +from . import DATA_AMCREST, SWITCHES + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['amcrest'] diff --git a/homeassistant/components/android_ip_webcam/binary_sensor.py b/homeassistant/components/android_ip_webcam/binary_sensor.py index e33e22f3778acd..c058c44c5034e1 100644 --- a/homeassistant/components/android_ip_webcam/binary_sensor.py +++ b/homeassistant/components/android_ip_webcam/binary_sensor.py @@ -1,7 +1,7 @@ """Support for Android IP Webcam binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.android_ip_webcam import ( - KEY_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST, CONF_NAME) + +from . import CONF_HOST, CONF_NAME, DATA_IP_WEBCAM, KEY_MAP, AndroidIPCamEntity DEPENDENCIES = ['android_ip_webcam'] diff --git a/homeassistant/components/android_ip_webcam/sensor.py b/homeassistant/components/android_ip_webcam/sensor.py index e98ce7951b8aa1..4d29493d64fba7 100644 --- a/homeassistant/components/android_ip_webcam/sensor.py +++ b/homeassistant/components/android_ip_webcam/sensor.py @@ -1,9 +1,10 @@ """Support for Android IP Webcam sensors.""" -from homeassistant.components.android_ip_webcam import ( - KEY_MAP, ICON_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST, - CONF_NAME, CONF_SENSORS) from homeassistant.helpers.icon import icon_for_battery_level +from . import ( + CONF_HOST, CONF_NAME, CONF_SENSORS, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP, + AndroidIPCamEntity) + DEPENDENCIES = ['android_ip_webcam'] diff --git a/homeassistant/components/android_ip_webcam/switch.py b/homeassistant/components/android_ip_webcam/switch.py index 73a94acbcdd9eb..0304c5747f71ae 100644 --- a/homeassistant/components/android_ip_webcam/switch.py +++ b/homeassistant/components/android_ip_webcam/switch.py @@ -1,8 +1,9 @@ """Support for Android IP Webcam settings.""" from homeassistant.components.switch import SwitchDevice -from homeassistant.components.android_ip_webcam import ( - KEY_MAP, ICON_MAP, DATA_IP_WEBCAM, AndroidIPCamEntity, CONF_HOST, - CONF_NAME, CONF_SWITCHES) + +from . import ( + CONF_HOST, CONF_NAME, CONF_SWITCHES, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP, + AndroidIPCamEntity) DEPENDENCIES = ['android_ip_webcam'] diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index 03ac5bd2549b9b..e00ce6ed13bcd2 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -1,8 +1,6 @@ """Support for Apple TV media player.""" import logging -from homeassistant.components.apple_tv import ( - ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES) from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK, @@ -14,6 +12,8 @@ from homeassistant.core import callback import homeassistant.util.dt as dt_util +from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES + DEPENDENCIES = ['apple_tv'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/apple_tv/remote.py b/homeassistant/components/apple_tv/remote.py index 2d80ded686116c..25b500ac09d6ea 100644 --- a/homeassistant/components/apple_tv/remote.py +++ b/homeassistant/components/apple_tv/remote.py @@ -1,8 +1,8 @@ """Remote control support for Apple TV.""" -from homeassistant.components.apple_tv import ( - ATTR_ATV, ATTR_POWER, DATA_APPLE_TV) from homeassistant.components import remote -from homeassistant.const import (CONF_NAME, CONF_HOST) +from homeassistant.const import CONF_HOST, CONF_NAME + +from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV DEPENDENCIES = ['apple_tv'] diff --git a/homeassistant/components/aqualogic/sensor.py b/homeassistant/components/aqualogic/sensor.py index 9e061ba91bfe87..dc06a2127e941d 100644 --- a/homeassistant/components/aqualogic/sensor.py +++ b/homeassistant/components/aqualogic/sensor.py @@ -4,12 +4,13 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_MONITORED_CONDITIONS, - TEMP_CELSIUS, TEMP_FAHRENHEIT) +from homeassistant.const import ( + CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.core import callback -from homeassistant.helpers.entity import Entity -import homeassistant.components.aqualogic as aq import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity + +from . import DOMAIN, UPDATE_TOPIC _LOGGER = logging.getLogger(__name__) @@ -46,7 +47,7 @@ async def async_setup_platform( """Set up the sensor platform.""" sensors = [] - processor = hass.data[aq.DOMAIN] + processor = hass.data[DOMAIN] for sensor_type in config.get(CONF_MONITORED_CONDITIONS): sensors.append(AquaLogicSensor(processor, sensor_type)) @@ -95,7 +96,7 @@ def icon(self): async def async_added_to_hass(self): """Register callbacks.""" self.hass.helpers.dispatcher.async_dispatcher_connect( - aq.UPDATE_TOPIC, self.async_update_callback) + UPDATE_TOPIC, self.async_update_callback) @callback def async_update_callback(self): diff --git a/homeassistant/components/aqualogic/switch.py b/homeassistant/components/aqualogic/switch.py index ee040fa1ba5b8f..21e573f944b6d2 100644 --- a/homeassistant/components/aqualogic/switch.py +++ b/homeassistant/components/aqualogic/switch.py @@ -3,11 +3,12 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback -import homeassistant.components.aqualogic as aq -from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA -from homeassistant.const import (CONF_MONITORED_CONDITIONS) +import homeassistant.helpers.config_validation as cv + +from . import DOMAIN, UPDATE_TOPIC DEPENDENCIES = ['aqualogic'] @@ -37,7 +38,7 @@ async def async_setup_platform( """Set up the switch platform.""" switches = [] - processor = hass.data[aq.DOMAIN] + processor = hass.data[DOMAIN] for switch_type in config.get(CONF_MONITORED_CONDITIONS): switches.append(AquaLogicSwitch(processor, switch_type)) @@ -101,7 +102,7 @@ def turn_off(self, **kwargs): async def async_added_to_hass(self): """Register callbacks.""" self.hass.helpers.dispatcher.async_dispatcher_connect( - aq.UPDATE_TOPIC, self.async_update_callback) + UPDATE_TOPIC, self.async_update_callback) @callback def async_update_callback(self): diff --git a/homeassistant/components/arlo/alarm_control_panel.py b/homeassistant/components/arlo/alarm_control_panel.py index 931dfa1b15d992..3557ed125c6cda 100644 --- a/homeassistant/components/arlo/alarm_control_panel.py +++ b/homeassistant/components/arlo/alarm_control_panel.py @@ -3,16 +3,16 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.alarm_control_panel import ( - AlarmControlPanel, PLATFORM_SCHEMA) -from homeassistant.components.arlo import ( - DATA_ARLO, ATTRIBUTION, SIGNAL_UPDATE_ARLO) + PLATFORM_SCHEMA, AlarmControlPanel) from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, - STATE_ALARM_DISARMED, STATE_ALARM_ARMED_NIGHT) + STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED) +from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ATTRIBUTION, DATA_ARLO, SIGNAL_UPDATE_ARLO _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/arlo/camera.py b/homeassistant/components/arlo/camera.py index 6f20ecdadcd3fc..43ccabb7390b98 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -3,16 +3,16 @@ import voluptuous as vol -from homeassistant.core import callback -import homeassistant.helpers.config_validation as cv -from homeassistant.components.arlo import ( - DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ATTR_BATTERY_LEVEL +from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO + _LOGGER = logging.getLogger(__name__) ARLO_MODE_ARMED = 'armed' diff --git a/homeassistant/components/arlo/sensor.py b/homeassistant/components/arlo/sensor.py index 1c3cc9334380dc..e08669eb80b60e 100644 --- a/homeassistant/components/arlo/sensor.py +++ b/homeassistant/components/arlo/sensor.py @@ -3,19 +3,18 @@ import voluptuous as vol -from homeassistant.core import callback -import homeassistant.helpers.config_validation as cv -from homeassistant.components.arlo import ( - ATTRIBUTION, DEFAULT_BRAND, DATA_ARLO, SIGNAL_UPDATE_ARLO) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY) - + ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS, DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS) +from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level +from . import ATTRIBUTION, DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['arlo'] diff --git a/homeassistant/components/asterisk_mbox/mailbox.py b/homeassistant/components/asterisk_mbox/mailbox.py index 9da4bd1a21a624..a3e7c3f4d61c13 100644 --- a/homeassistant/components/asterisk_mbox/mailbox.py +++ b/homeassistant/components/asterisk_mbox/mailbox.py @@ -1,12 +1,13 @@ """Support for the Asterisk Voicemail interface.""" import logging -from homeassistant.components.asterisk_mbox import DOMAIN as ASTERISK_DOMAIN from homeassistant.components.mailbox import ( CONTENT_TYPE_MPEG, Mailbox, StreamError) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DOMAIN as ASTERISK_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['asterisk_mbox'] diff --git a/homeassistant/components/asuswrt/device_tracker.py b/homeassistant/components/asuswrt/device_tracker.py index 4630c3730ca7e1..f5c6dd4a42a122 100644 --- a/homeassistant/components/asuswrt/device_tracker.py +++ b/homeassistant/components/asuswrt/device_tracker.py @@ -6,9 +6,10 @@ """ import logging -from homeassistant.components.asuswrt import DATA_ASUSWRT from homeassistant.components.device_tracker import DeviceScanner +from . import DATA_ASUSWRT + DEPENDENCIES = ['asuswrt'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/asuswrt/sensor.py b/homeassistant/components/asuswrt/sensor.py index 08e2ec27cb2ee2..53d232862c6b72 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -7,7 +7,8 @@ import logging from homeassistant.helpers.entity import Entity -from homeassistant.components.asuswrt import DATA_ASUSWRT + +from . import DATA_ASUSWRT DEPENDENCIES = ['asuswrt'] diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index 1ad2d80cea8c03..3a69d41177d6c5 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -1,9 +1,10 @@ """Support for August binary sensors.""" +from datetime import datetime, timedelta import logging -from datetime import timedelta, datetime -from homeassistant.components.august import DATA_AUGUST -from homeassistant.components.binary_sensor import (BinarySensorDevice) +from homeassistant.components.binary_sensor import BinarySensorDevice + +from . import DATA_AUGUST _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index 7420bb040674d5..53a9d78bc60419 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -3,9 +3,10 @@ import requests -from homeassistant.components.august import DATA_AUGUST, DEFAULT_TIMEOUT from homeassistant.components.camera import Camera +from . import DATA_AUGUST, DEFAULT_TIMEOUT + DEPENDENCIES = ['august'] SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index ff355bbf87bf37..e112eaa2592c31 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -1,11 +1,12 @@ """Support for August lock.""" -import logging from datetime import timedelta +import logging -from homeassistant.components.august import DATA_AUGUST from homeassistant.components.lock import LockDevice from homeassistant.const import ATTR_BATTERY_LEVEL +from . import DATA_AUGUST + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['august'] diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 5a7b19ce4e3ca6..94776deefb0e23 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -6,20 +6,20 @@ import voluptuous as vol -from homeassistant.setup import async_prepare_setup_platform -from homeassistant.core import CoreState, Context -from homeassistant.loader import bind_hass from homeassistant.const import ( - ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, - SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID, - EVENT_AUTOMATION_TRIGGERED, ATTR_NAME) + ATTR_ENTITY_ID, ATTR_NAME, CONF_ID, CONF_PLATFORM, + EVENT_AUTOMATION_TRIGGERED, EVENT_HOMEASSISTANT_START, SERVICE_RELOAD, + SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON) +from homeassistant.core import Context, CoreState from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import extract_domain_configs, script, condition +from homeassistant.helpers import condition, extract_domain_configs, script +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.loader import bind_hass +from homeassistant.setup import async_prepare_setup_platform from homeassistant.util.dt import utcnow -import homeassistant.helpers.config_validation as cv DOMAIN = 'automation' DEPENDENCIES = ['group'] @@ -54,9 +54,8 @@ def _platform_validator(config): """Validate it is a valid platform.""" try: - platform = importlib.import_module( - 'homeassistant.components.automation.{}'.format( - config[CONF_PLATFORM])) + platform = importlib.import_module('.{}'.format(config[CONF_PLATFORM]), + __name__) except ImportError: raise vol.Invalid('Invalid platform specified') from None diff --git a/homeassistant/components/blink/alarm_control_panel.py b/homeassistant/components/blink/alarm_control_panel.py index b9bf4a5250fc59..75e645dff5f3f9 100644 --- a/homeassistant/components/blink/alarm_control_panel.py +++ b/homeassistant/components/blink/alarm_control_panel.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.blink import ( - BLINK_DATA, DEFAULT_ATTRIBUTION) from homeassistant.const import ( - ATTR_ATTRIBUTION, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY) + ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED) + +from . import BLINK_DATA, DEFAULT_ATTRIBUTION _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/blink/binary_sensor.py b/homeassistant/components/blink/binary_sensor.py index fe0b95b1517f51..466b73caf5fd5a 100644 --- a/homeassistant/components/blink/binary_sensor.py +++ b/homeassistant/components/blink/binary_sensor.py @@ -1,8 +1,9 @@ """Support for Blink system camera control.""" -from homeassistant.components.blink import BLINK_DATA, BINARY_SENSORS from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.const import CONF_MONITORED_CONDITIONS +from . import BINARY_SENSORS, BLINK_DATA + DEPENDENCIES = ['blink'] diff --git a/homeassistant/components/blink/camera.py b/homeassistant/components/blink/camera.py index 2e5c024d6e509b..1da3080e3ff074 100644 --- a/homeassistant/components/blink/camera.py +++ b/homeassistant/components/blink/camera.py @@ -1,9 +1,10 @@ """Support for Blink system camera.""" import logging -from homeassistant.components.blink import BLINK_DATA, DEFAULT_BRAND from homeassistant.components.camera import Camera +from . import BLINK_DATA, DEFAULT_BRAND + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['blink'] diff --git a/homeassistant/components/blink/sensor.py b/homeassistant/components/blink/sensor.py index c1fdf9252ddf77..0e97db9d7d410d 100644 --- a/homeassistant/components/blink/sensor.py +++ b/homeassistant/components/blink/sensor.py @@ -1,9 +1,10 @@ """Support for Blink system camera sensors.""" import logging -from homeassistant.components.blink import BLINK_DATA, SENSORS -from homeassistant.helpers.entity import Entity from homeassistant.const import CONF_MONITORED_CONDITIONS +from homeassistant.helpers.entity import Entity + +from . import BLINK_DATA, SENSORS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/binary_sensor.py b/homeassistant/components/bmw_connected_drive/binary_sensor.py index 1843f647df8d89..deab157292d2f3 100644 --- a/homeassistant/components/bmw_connected_drive/binary_sensor.py +++ b/homeassistant/components/bmw_connected_drive/binary_sensor.py @@ -2,9 +2,10 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.const import LENGTH_KILOMETERS +from . import DOMAIN as BMW_DOMAIN + DEPENDENCIES = ['bmw_connected_drive'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/device_tracker.py b/homeassistant/components/bmw_connected_drive/device_tracker.py index 21121b069af2c6..20e84e33e29bd3 100644 --- a/homeassistant/components/bmw_connected_drive/device_tracker.py +++ b/homeassistant/components/bmw_connected_drive/device_tracker.py @@ -1,10 +1,10 @@ """Device tracker for BMW Connected Drive vehicles.""" import logging -from homeassistant.components.bmw_connected_drive import DOMAIN \ - as BMW_DOMAIN from homeassistant.util import slugify +from . import DOMAIN as BMW_DOMAIN + DEPENDENCIES = ['bmw_connected_drive'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/lock.py b/homeassistant/components/bmw_connected_drive/lock.py index 8a5eddaa86a112..fe646dcd1c9c10 100644 --- a/homeassistant/components/bmw_connected_drive/lock.py +++ b/homeassistant/components/bmw_connected_drive/lock.py @@ -1,10 +1,11 @@ """Support for BMW car locks with BMW ConnectedDrive.""" import logging -from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.components.lock import LockDevice from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED +from . import DOMAIN as BMW_DOMAIN + DEPENDENCIES = ['bmw_connected_drive'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/sensor.py b/homeassistant/components/bmw_connected_drive/sensor.py index a01142c53ed229..03c03f01b4a148 100644 --- a/homeassistant/components/bmw_connected_drive/sensor.py +++ b/homeassistant/components/bmw_connected_drive/sensor.py @@ -1,12 +1,13 @@ """Support for reading vehicle status from BMW connected drive portal.""" import logging -from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN +from homeassistant.const import ( + CONF_UNIT_SYSTEM_IMPERIAL, LENGTH_KILOMETERS, LENGTH_MILES, VOLUME_GALLONS, + VOLUME_LITERS) from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level -from homeassistant.const import (CONF_UNIT_SYSTEM_IMPERIAL, VOLUME_LITERS, - VOLUME_GALLONS, LENGTH_KILOMETERS, - LENGTH_MILES) + +from . import DOMAIN as BMW_DOMAIN DEPENDENCIES = ['bmw_connected_drive'] diff --git a/homeassistant/components/bom/weather.py b/homeassistant/components/bom/weather.py index b35e7928ec1739..2444192d87d22f 100644 --- a/homeassistant/components/bom/weather.py +++ b/homeassistant/components/bom/weather.py @@ -3,14 +3,15 @@ import voluptuous as vol -# Reuse data and API logic from the sensor implementation -from homeassistant.components.bom.sensor import ( - CONF_STATION, BOMCurrentData, closest_station, validate_station) from homeassistant.components.weather import PLATFORM_SCHEMA, WeatherEntity from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers import config_validation as cv +# Reuse data and API logic from the sensor implementation +from .sensor import ( + CONF_STATION, BOMCurrentData, closest_station, validate_station) + _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index eeb65ab3ce5e0e..fa386a177057d3 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -8,19 +8,18 @@ from datetime import datetime, timedelta import logging -import async_timeout import aiohttp +import async_timeout import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE, - CONF_MONITORED_CONDITIONS, CONF_NAME, TEMP_CELSIUS) + ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS, + CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import ( - async_track_point_in_utc_time) +from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util import dt as dt_util REQUIREMENTS = ['buienradar==0.91'] @@ -144,7 +143,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Create the buienradar sensor.""" - from homeassistant.components.buienradar.weather import DEFAULT_TIMEFRAME + from ..weather import DEFAULT_TIMEFRAME latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) diff --git a/homeassistant/components/buienradar/weather.py b/homeassistant/components/buienradar/weather.py index 585f9ac55b989f..86dcb229a78bbd 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -3,8 +3,6 @@ import voluptuous as vol -# Reuse data and API logic from the sensor implementation -from homeassistant.components.buienradar.sensor import BrData from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, PLATFORM_SCHEMA, WeatherEntity) @@ -12,6 +10,9 @@ CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers import config_validation as cv +# Reuse data and API logic from the sensor implementation +from .sensor import BrData + REQUIREMENTS = ['buienradar==0.91'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/calendar/demo.py b/homeassistant/components/calendar/demo.py index bf9d4abeb58475..bd5724ca455815 100644 --- a/homeassistant/components/calendar/demo.py +++ b/homeassistant/components/calendar/demo.py @@ -6,9 +6,10 @@ """ import copy -import homeassistant.util.dt as dt_util -from homeassistant.components.calendar import CalendarEventDevice, get_date from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME +import homeassistant.util.dt as dt_util + +from . import CalendarEventDevice, get_date def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py index f950edb5c6ca79..f9be3f47c358fd 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/camera/demo.py @@ -7,7 +7,7 @@ import logging import os -from homeassistant.components.camera import Camera, SUPPORT_ON_OFF +from . import SUPPORT_ON_OFF, Camera _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/canary/alarm_control_panel.py b/homeassistant/components/canary/alarm_control_panel.py index b22a76fdb3b0aa..617942246667db 100644 --- a/homeassistant/components/canary/alarm_control_panel.py +++ b/homeassistant/components/canary/alarm_control_panel.py @@ -7,9 +7,11 @@ import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.canary import DATA_CANARY -from homeassistant.const import STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, \ - STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_HOME +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_DISARMED) + +from . import DATA_CANARY DEPENDENCIES = ['canary'] diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index eb0c8f3fc6d7a3..c54565d6fde990 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -5,18 +5,19 @@ https://home-assistant.io/components/camera.canary/ """ import asyncio -import logging from datetime import timedelta +import logging import voluptuous as vol -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA -from homeassistant.components.canary import DATA_CANARY, DEFAULT_TIMEOUT +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream from homeassistant.util import Throttle +from . import DATA_CANARY, DEFAULT_TIMEOUT + CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' DEPENDENCIES = ['canary', 'ffmpeg'] diff --git a/homeassistant/components/canary/sensor.py b/homeassistant/components/canary/sensor.py index 015c6b378e090b..d24c00c926648d 100644 --- a/homeassistant/components/canary/sensor.py +++ b/homeassistant/components/canary/sensor.py @@ -5,11 +5,12 @@ https://home-assistant.io/components/sensor.canary/ """ -from homeassistant.components.canary import DATA_CANARY from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level +from . import DATA_CANARY + DEPENDENCIES = ['canary'] SENSOR_VALUE_PRECISION = 2 diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 28373cc6c14b20..77332883a910f6 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -7,14 +7,13 @@ import attr import voluptuous as vol -from homeassistant.components.cast import DOMAIN as CAST_DOMAIN from homeassistant.components.media_player import ( - MediaPlayerDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, MediaPlayerDevice) from homeassistant.components.media_player.const import ( - MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, - SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, - SUPPORT_PREVIOUS_TRACK, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, - SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) + MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, SUPPORT_NEXT_TRACK, + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, + SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, + SUPPORT_VOLUME_SET) from homeassistant.const import ( CONF_HOST, EVENT_HOMEASSISTANT_STOP, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) @@ -26,6 +25,8 @@ from homeassistant.helpers.typing import ConfigType, HomeAssistantType import homeassistant.util.dt as dt_util +from . import DOMAIN as CAST_DOMAIN + DEPENDENCIES = ('cast',) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/climate/demo.py b/homeassistant/components/climate/demo.py index 5b4775982a6b32..2dd31c1b20d8ca 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/climate/demo.py @@ -4,16 +4,16 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.climate import ClimateDevice -from homeassistant.components.climate.const import ( - ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY, - SUPPORT_TARGET_HUMIDITY_LOW, SUPPORT_TARGET_HUMIDITY_HIGH, - SUPPORT_AWAY_MODE, SUPPORT_HOLD_MODE, SUPPORT_FAN_MODE, - SUPPORT_OPERATION_MODE, SUPPORT_AUX_HEAT, SUPPORT_SWING_MODE, - SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW, - SUPPORT_ON_OFF) -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT + +from . import ClimateDevice +from .const import ( + ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, SUPPORT_AUX_HEAT, + SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, SUPPORT_ON_OFF, + SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_HUMIDITY, + SUPPORT_TARGET_HUMIDITY_HIGH, SUPPORT_TARGET_HUMIDITY_LOW, + SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_HIGH, + SUPPORT_TARGET_TEMPERATURE_LOW) SUPPORT_FLAGS = SUPPORT_TARGET_HUMIDITY_LOW | SUPPORT_TARGET_HUMIDITY_HIGH diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index da4f79ec1e6b42..af0a3eea6ab09b 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -9,23 +9,23 @@ import voluptuous as vol -from homeassistant.core import callback -from homeassistant.core import DOMAIN as HA_DOMAIN -from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA -from homeassistant.components.climate.const import ( - STATE_HEAT, STATE_COOL, STATE_IDLE, STATE_AUTO, - ATTR_OPERATION_MODE, ATTR_AWAY_MODE, SUPPORT_OPERATION_MODE, - SUPPORT_AWAY_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME, ATTR_ENTITY_ID, - SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_UNKNOWN, PRECISION_HALVES, - PRECISION_TENTHS, PRECISION_WHOLE) + ATTR_ENTITY_ID, ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, + PRECISION_TENTHS, PRECISION_WHOLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, + STATE_OFF, STATE_ON, STATE_UNKNOWN) +from homeassistant.core import DOMAIN as HA_DOMAIN, callback from homeassistant.helpers import condition +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import ( async_track_state_change, async_track_time_interval) -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity +from . import PLATFORM_SCHEMA, ClimateDevice +from .const import ( + ATTR_AWAY_MODE, ATTR_OPERATION_MODE, STATE_AUTO, STATE_COOL, STATE_HEAT, + STATE_IDLE, SUPPORT_AWAY_MODE, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['switch', 'sensor'] diff --git a/homeassistant/components/comfoconnect/fan.py b/homeassistant/components/comfoconnect/fan.py index a396dd276a5444..88dcffcfd21485 100644 --- a/homeassistant/components/comfoconnect/fan.py +++ b/homeassistant/components/comfoconnect/fan.py @@ -1,12 +1,12 @@ """Platform to control a Zehnder ComfoAir Q350/450/600 ventilation unit.""" import logging -from homeassistant.components.comfoconnect import ( - DOMAIN, ComfoConnectBridge, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED) from homeassistant.components.fan import ( - FanEntity, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, - SUPPORT_SET_SPEED) -from homeassistant.helpers.dispatcher import (dispatcher_connect) + SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_SET_SPEED, + FanEntity) +from homeassistant.helpers.dispatcher import dispatcher_connect + +from . import DOMAIN, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, ComfoConnectBridge _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/comfoconnect/sensor.py b/homeassistant/components/comfoconnect/sensor.py index ac5de866cfb71e..edb96b8d279d3d 100644 --- a/homeassistant/components/comfoconnect/sensor.py +++ b/homeassistant/components/comfoconnect/sensor.py @@ -1,15 +1,15 @@ """Platform to control a Zehnder ComfoAir Q350/450/600 ventilation unit.""" import logging -from homeassistant.components.comfoconnect import ( - DOMAIN, ComfoConnectBridge, ATTR_CURRENT_TEMPERATURE, - ATTR_CURRENT_HUMIDITY, ATTR_OUTSIDE_TEMPERATURE, - ATTR_OUTSIDE_HUMIDITY, ATTR_AIR_FLOW_SUPPLY, - ATTR_AIR_FLOW_EXHAUST, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED) from homeassistant.const import CONF_RESOURCES, TEMP_CELSIUS from homeassistant.helpers.dispatcher import dispatcher_connect from homeassistant.helpers.entity import Entity +from . import ( + ATTR_AIR_FLOW_EXHAUST, ATTR_AIR_FLOW_SUPPLY, ATTR_CURRENT_HUMIDITY, + ATTR_CURRENT_TEMPERATURE, ATTR_OUTSIDE_HUMIDITY, ATTR_OUTSIDE_TEMPERATURE, + DOMAIN, SIGNAL_COMFOCONNECT_UPDATE_RECEIVED, ComfoConnectBridge) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['comfoconnect'] diff --git a/homeassistant/components/command_line/binary_sensor.py b/homeassistant/components/command_line/binary_sensor.py index af9b3d48dea516..21ee1312e7a488 100644 --- a/homeassistant/components/command_line/binary_sensor.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -4,18 +4,19 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.command_line/ """ -import logging from datetime import timedelta +import logging import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import ( - BinarySensorDevice, DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA) -from homeassistant.components.command_line.sensor import CommandSensorData + DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import ( - CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, CONF_NAME, CONF_VALUE_TEMPLATE, - CONF_COMMAND, CONF_DEVICE_CLASS) + CONF_COMMAND, CONF_DEVICE_CLASS, CONF_NAME, CONF_PAYLOAD_OFF, + CONF_PAYLOAD_ON, CONF_VALUE_TEMPLATE) +import homeassistant.helpers.config_validation as cv + +from .sensor import CommandSensorData _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/config/automation.py b/homeassistant/components/config/automation.py index 47ac9d3a4b2262..175d90ff59ca2e 100644 --- a/homeassistant/components/config/automation.py +++ b/homeassistant/components/config/automation.py @@ -2,11 +2,12 @@ from collections import OrderedDict import uuid -from homeassistant.components.config import EditIdBasedConfigView -from homeassistant.const import CONF_ID, SERVICE_RELOAD from homeassistant.components.automation import DOMAIN, PLATFORM_SCHEMA +from homeassistant.const import CONF_ID, SERVICE_RELOAD import homeassistant.helpers.config_validation as cv +from . import EditIdBasedConfigView + CONFIG_PATH = 'automations.yaml' diff --git a/homeassistant/components/config/customize.py b/homeassistant/components/config/customize.py index ec9d9d0ff27cd9..bb774ae7ef8e0d 100644 --- a/homeassistant/components/config/customize.py +++ b/homeassistant/components/config/customize.py @@ -1,11 +1,11 @@ """Provide configuration end points for Customize.""" -from homeassistant.components.config import EditKeyBasedConfigView from homeassistant.components import SERVICE_RELOAD_CORE_CONFIG from homeassistant.config import DATA_CUSTOMIZE from homeassistant.core import DOMAIN - import homeassistant.helpers.config_validation as cv +from . import EditKeyBasedConfigView + CONFIG_PATH = 'customize.yaml' diff --git a/homeassistant/components/config/group.py b/homeassistant/components/config/group.py index f9b9a2c491867e..60421bcc125598 100644 --- a/homeassistant/components/config/group.py +++ b/homeassistant/components/config/group.py @@ -1,9 +1,9 @@ """Provide configuration end points for Groups.""" -from homeassistant.const import SERVICE_RELOAD -from homeassistant.components.config import EditKeyBasedConfigView from homeassistant.components.group import DOMAIN, GROUP_SCHEMA +from homeassistant.const import SERVICE_RELOAD import homeassistant.helpers.config_validation as cv +from . import EditKeyBasedConfigView CONFIG_PATH = 'groups.yaml' diff --git a/homeassistant/components/config/script.py b/homeassistant/components/config/script.py index 640e267d06613e..c8a58e5d72a2f3 100644 --- a/homeassistant/components/config/script.py +++ b/homeassistant/components/config/script.py @@ -1,9 +1,9 @@ """Provide configuration end points for scripts.""" -from homeassistant.components.config import EditKeyBasedConfigView from homeassistant.components.script import DOMAIN, SCRIPT_ENTRY_SCHEMA from homeassistant.const import SERVICE_RELOAD import homeassistant.helpers.config_validation as cv +from . import EditKeyBasedConfigView CONFIG_PATH = 'scripts.yaml' diff --git a/homeassistant/components/config/zwave.py b/homeassistant/components/config/zwave.py index f29dde4594c2a9..e7e39968401d7f 100644 --- a/homeassistant/components/config/zwave.py +++ b/homeassistant/components/config/zwave.py @@ -4,13 +4,14 @@ from aiohttp.web import Response -from homeassistant.components.config import EditKeyBasedConfigView from homeassistant.components.http import HomeAssistantView from homeassistant.components.zwave import DEVICE_CONFIG_SCHEMA_ENTRY, const from homeassistant.const import HTTP_NOT_FOUND, HTTP_OK import homeassistant.core as ha import homeassistant.helpers.config_validation as cv +from . import EditKeyBasedConfigView + _LOGGER = logging.getLogger(__name__) CONFIG_PATH = 'zwave_device_config.yaml' OZW_LOG_FILENAME = 'OZW_Log.txt' diff --git a/homeassistant/components/conversation/__init__.py b/homeassistant/components/conversation/__init__.py index 7082cb367264fe..bb2d692f2490b3 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -6,17 +6,16 @@ from homeassistant import core from homeassistant.components import http -from homeassistant.components.conversation.util import create_matcher -from homeassistant.components.http.data_validator import ( - RequestDataValidator) -from homeassistant.components.cover import (INTENT_OPEN_COVER, - INTENT_CLOSE_COVER) +from homeassistant.components.cover import ( + INTENT_CLOSE_COVER, INTENT_OPEN_COVER) +from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.const import EVENT_COMPONENT_LOADED from homeassistant.core import callback -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers import intent +from homeassistant.helpers import config_validation as cv, intent from homeassistant.loader import bind_hass -from homeassistant.setup import (ATTR_COMPONENT) +from homeassistant.setup import ATTR_COMPONENT + +from .util import create_matcher _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/cover/demo.py b/homeassistant/components/cover/demo.py index 21add0a6c62ae5..1f31cecc9966c7 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/cover/demo.py @@ -4,11 +4,12 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.cover import ( - CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, ATTR_POSITION, - ATTR_TILT_POSITION) from homeassistant.helpers.event import track_utc_time_change +from . import ( + ATTR_POSITION, ATTR_TILT_POSITION, SUPPORT_CLOSE, SUPPORT_OPEN, + CoverDevice) + def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Demo covers.""" diff --git a/homeassistant/components/cover/group.py b/homeassistant/components/cover/group.py index 0424c900747bc2..aba57284da8f58 100644 --- a/homeassistant/components/cover/group.py +++ b/homeassistant/components/cover/group.py @@ -8,22 +8,22 @@ import voluptuous as vol -from homeassistant.core import callback -from homeassistant.components.cover import ( - DOMAIN, PLATFORM_SCHEMA, CoverDevice, ATTR_POSITION, - ATTR_CURRENT_POSITION, ATTR_TILT_POSITION, ATTR_CURRENT_TILT_POSITION, - SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP, SUPPORT_SET_POSITION, - SUPPORT_OPEN_TILT, SUPPORT_CLOSE_TILT, - SUPPORT_STOP_TILT, SUPPORT_SET_TILT_POSITION, - SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, SERVICE_SET_COVER_POSITION, - SERVICE_STOP_COVER, SERVICE_OPEN_COVER_TILT, SERVICE_CLOSE_COVER_TILT, - SERVICE_STOP_COVER_TILT, SERVICE_SET_COVER_TILT_POSITION) from homeassistant.const import ( - ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, - CONF_ENTITIES, CONF_NAME, STATE_CLOSED) + ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, + CONF_NAME, STATE_CLOSED) +from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_state_change +from . import ( + ATTR_CURRENT_POSITION, ATTR_CURRENT_TILT_POSITION, ATTR_POSITION, + ATTR_TILT_POSITION, DOMAIN, PLATFORM_SCHEMA, SERVICE_CLOSE_COVER, + SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, SERVICE_OPEN_COVER_TILT, + SERVICE_SET_COVER_POSITION, SERVICE_SET_COVER_TILT_POSITION, + SERVICE_STOP_COVER, SERVICE_STOP_COVER_TILT, SUPPORT_CLOSE, + SUPPORT_CLOSE_TILT, SUPPORT_OPEN, SUPPORT_OPEN_TILT, SUPPORT_SET_POSITION, + SUPPORT_SET_TILT_POSITION, SUPPORT_STOP, SUPPORT_STOP_TILT, CoverDevice) + _LOGGER = logging.getLogger(__name__) KEY_OPEN_CLOSE = 'open_close' diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index 869c38869cbcf4..c42f2785576dfd 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -4,19 +4,20 @@ import voluptuous as vol -from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA +from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice from homeassistant.components.climate.const import ( ATTR_CURRENT_TEMPERATURE, ATTR_FAN_MODE, ATTR_OPERATION_MODE, - ATTR_SWING_MODE, STATE_AUTO, STATE_COOL, STATE_DRY, - STATE_FAN_ONLY, STATE_HEAT, SUPPORT_FAN_MODE, - SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.daikin import DOMAIN as DAIKIN_DOMAIN -from homeassistant.components.daikin.const import ( - ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE) + ATTR_SWING_MODE, STATE_AUTO, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, + STATE_HEAT, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, + SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, STATE_OFF, TEMP_CELSIUS) import homeassistant.helpers.config_validation as cv +from . import DOMAIN as DAIKIN_DOMAIN +from .const import ( + ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, ATTR_TARGET_TEMPERATURE) + _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index 3669dfac280db3..5a005e29989ef3 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -1,14 +1,15 @@ """Support for Daikin AC sensors.""" import logging -from homeassistant.components.daikin import DOMAIN as DAIKIN_DOMAIN -from homeassistant.components.daikin.const import ( - ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, SENSOR_TYPE_TEMPERATURE, - SENSOR_TYPES) from homeassistant.const import CONF_ICON, CONF_NAME, CONF_TYPE from homeassistant.helpers.entity import Entity from homeassistant.util.unit_system import UnitSystem +from . import DOMAIN as DAIKIN_DOMAIN +from .const import ( + ATTR_INSIDE_TEMPERATURE, ATTR_OUTSIDE_TEMPERATURE, SENSOR_TYPE_TEMPERATURE, + SENSOR_TYPES) + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/danfoss_air/binary_sensor.py b/homeassistant/components/danfoss_air/binary_sensor.py index 4052a100540cdb..723b0d0880154b 100644 --- a/homeassistant/components/danfoss_air/binary_sensor.py +++ b/homeassistant/components/danfoss_air/binary_sensor.py @@ -1,7 +1,7 @@ """Support for the for Danfoss Air HRV binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.danfoss_air import DOMAIN \ - as DANFOSS_AIR_DOMAIN + +from . import DOMAIN as DANFOSS_AIR_DOMAIN def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/danfoss_air/sensor.py b/homeassistant/components/danfoss_air/sensor.py index 9902184e62410c..a5dc2a2eb097bd 100644 --- a/homeassistant/components/danfoss_air/sensor.py +++ b/homeassistant/components/danfoss_air/sensor.py @@ -1,13 +1,13 @@ """Support for the for Danfoss Air HRV sensors.""" import logging -from homeassistant.components.danfoss_air import DOMAIN \ - as DANFOSS_AIR_DOMAIN from homeassistant.const import ( - TEMP_CELSIUS, DEVICE_CLASS_BATTERY, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY) + DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, + TEMP_CELSIUS) from homeassistant.helpers.entity import Entity +from . import DOMAIN as DANFOSS_AIR_DOMAIN + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/danfoss_air/switch.py b/homeassistant/components/danfoss_air/switch.py index ec85757be59dd1..f5a7fd47f69c73 100644 --- a/homeassistant/components/danfoss_air/switch.py +++ b/homeassistant/components/danfoss_air/switch.py @@ -1,10 +1,9 @@ """Support for the for Danfoss Air HRV sswitches.""" import logging -from homeassistant.components.switch import ( - SwitchDevice) -from homeassistant.components.danfoss_air import DOMAIN \ - as DANFOSS_AIR_DOMAIN +from homeassistant.components.switch import SwitchDevice + +from . import DOMAIN as DANFOSS_AIR_DOMAIN _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index bf0799d1fa26f4..e728430f202aba 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -1,7 +1,7 @@ """Constants for the deCONZ component.""" import logging -_LOGGER = logging.getLogger('homeassistant.components.deconz') +_LOGGER = logging.getLogger('.') DOMAIN = 'deconz' diff --git a/homeassistant/components/digital_ocean/binary_sensor.py b/homeassistant/components/digital_ocean/binary_sensor.py index 88df56cc629d0d..d496a09161b91d 100644 --- a/homeassistant/components/digital_ocean/binary_sensor.py +++ b/homeassistant/components/digital_ocean/binary_sensor.py @@ -3,14 +3,15 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.digital_ocean import ( - CONF_DROPLETS, ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, - ATTR_FEATURES, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, - ATTR_REGION, ATTR_VCPUS, ATTRIBUTION, DATA_DIGITAL_OCEAN) + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import ATTR_ATTRIBUTION +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, ATTR_FEATURES, + ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_REGION, ATTR_VCPUS, + ATTRIBUTION, CONF_DROPLETS, DATA_DIGITAL_OCEAN) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/digital_ocean/switch.py b/homeassistant/components/digital_ocean/switch.py index 9b5ddda3408066..bc4a6a29b42064 100644 --- a/homeassistant/components/digital_ocean/switch.py +++ b/homeassistant/components/digital_ocean/switch.py @@ -3,13 +3,14 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.components.digital_ocean import ( - CONF_DROPLETS, ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, - ATTR_FEATURES, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, - ATTR_REGION, ATTR_VCPUS, ATTRIBUTION, DATA_DIGITAL_OCEAN) +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import ATTR_ATTRIBUTION +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, ATTR_FEATURES, + ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_REGION, ATTR_VCPUS, + ATTRIBUTION, CONF_DROPLETS, DATA_DIGITAL_OCEAN) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/doorbird/camera.py b/homeassistant/components/doorbird/camera.py index e201837aaf626a..272a3cb932a0df 100644 --- a/homeassistant/components/doorbird/camera.py +++ b/homeassistant/components/doorbird/camera.py @@ -7,9 +7,10 @@ import async_timeout from homeassistant.components.camera import Camera -from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN from homeassistant.helpers.aiohttp_client import async_get_clientsession +from . import DOMAIN as DOORBIRD_DOMAIN + DEPENDENCIES = ['doorbird'] _CAMERA_LAST_VISITOR = "{} Last Ring" diff --git a/homeassistant/components/doorbird/switch.py b/homeassistant/components/doorbird/switch.py index 376713d4b27b39..ba6f96660d1b2d 100644 --- a/homeassistant/components/doorbird/switch.py +++ b/homeassistant/components/doorbird/switch.py @@ -2,9 +2,10 @@ import datetime import logging -from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as DOORBIRD_DOMAIN + DEPENDENCIES = ['doorbird'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dovado/notify.py b/homeassistant/components/dovado/notify.py index ea6ba2b455fa51..59827529ed3ad5 100644 --- a/homeassistant/components/dovado/notify.py +++ b/homeassistant/components/dovado/notify.py @@ -1,9 +1,10 @@ """Support for SMS notifications from the Dovado router.""" import logging -from homeassistant.components.dovado import DOMAIN as DOVADO_DOMAIN -from homeassistant.components.notify import BaseNotificationService, \ - ATTR_TARGET +from homeassistant.components.notify import ( + ATTR_TARGET, BaseNotificationService) + +from . import DOMAIN as DOVADO_DOMAIN _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dovado/sensor.py b/homeassistant/components/dovado/sensor.py index eb0016ed29816a..56c4ee03a3ada9 100644 --- a/homeassistant/components/dovado/sensor.py +++ b/homeassistant/components/dovado/sensor.py @@ -1,16 +1,17 @@ """Support for sensors from the Dovado router.""" +from datetime import timedelta import logging import re -from datetime import timedelta import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.dovado import DOMAIN as DOVADO_DOMAIN from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_SENSORS +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import DOMAIN as DOVADO_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['dovado'] diff --git a/homeassistant/components/dyson/climate.py b/homeassistant/components/dyson/climate.py index 09196a82bed8bd..3e5c976b1f412e 100644 --- a/homeassistant/components/dyson/climate.py +++ b/homeassistant/components/dyson/climate.py @@ -6,12 +6,13 @@ """ import logging -from homeassistant.components.dyson import DYSON_DEVICES from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_HEAT, STATE_COOL, STATE_IDLE, - SUPPORT_TARGET_TEMPERATURE, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE) -from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE + STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_FAN_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS + +from . import DYSON_DEVICES _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dyson/fan.py b/homeassistant/components/dyson/fan.py index ef517021178f98..743d301df42fe4 100644 --- a/homeassistant/components/dyson/fan.py +++ b/homeassistant/components/dyson/fan.py @@ -7,11 +7,12 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.dyson import DYSON_DEVICES from homeassistant.components.fan import ( DOMAIN, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity) from homeassistant.const import CONF_ENTITY_ID +import homeassistant.helpers.config_validation as cv + +from . import DYSON_DEVICES _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dyson/sensor.py b/homeassistant/components/dyson/sensor.py index 53913d47b72cd1..ed8987f75c23ec 100644 --- a/homeassistant/components/dyson/sensor.py +++ b/homeassistant/components/dyson/sensor.py @@ -6,10 +6,11 @@ """ import logging -from homeassistant.components.dyson import DYSON_DEVICES from homeassistant.const import STATE_OFF, TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import DYSON_DEVICES + DEPENDENCIES = ['dyson'] SENSOR_UNITS = { diff --git a/homeassistant/components/dyson/vacuum.py b/homeassistant/components/dyson/vacuum.py index 3d6e23c20c8e22..7902cfa1585d28 100644 --- a/homeassistant/components/dyson/vacuum.py +++ b/homeassistant/components/dyson/vacuum.py @@ -6,13 +6,14 @@ """ import logging -from homeassistant.components.dyson import DYSON_DEVICES from homeassistant.components.vacuum import ( SUPPORT_BATTERY, SUPPORT_FAN_SPEED, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, VacuumDevice) from homeassistant.helpers.icon import icon_for_battery_level +from . import DYSON_DEVICES + _LOGGER = logging.getLogger(__name__) ATTR_CLEAN_ID = 'clean_id' diff --git a/homeassistant/components/ecoal_boiler/sensor.py b/homeassistant/components/ecoal_boiler/sensor.py index 47ed2d6ebdf6d0..ef8b39842d9a0f 100644 --- a/homeassistant/components/ecoal_boiler/sensor.py +++ b/homeassistant/components/ecoal_boiler/sensor.py @@ -1,11 +1,11 @@ """Allows reading temperatures from ecoal/esterownik.pl controller.""" import logging -from homeassistant.components.ecoal_boiler import ( - DATA_ECOAL_BOILER, AVAILABLE_SENSORS, ) from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import AVAILABLE_SENSORS, DATA_ECOAL_BOILER + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ecoal_boiler'] diff --git a/homeassistant/components/ecoal_boiler/switch.py b/homeassistant/components/ecoal_boiler/switch.py index f113125194a179..db8759a032acc9 100644 --- a/homeassistant/components/ecoal_boiler/switch.py +++ b/homeassistant/components/ecoal_boiler/switch.py @@ -3,8 +3,8 @@ from typing import Optional from homeassistant.components.switch import SwitchDevice -from homeassistant.components.ecoal_boiler import ( - DATA_ECOAL_BOILER, AVAILABLE_PUMPS, ) + +from . import AVAILABLE_PUMPS, DATA_ECOAL_BOILER _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ecovacs/vacuum.py b/homeassistant/components/ecovacs/vacuum.py index 9d2af730315707..b9fe94f2bed0cb 100644 --- a/homeassistant/components/ecovacs/vacuum.py +++ b/homeassistant/components/ecovacs/vacuum.py @@ -2,13 +2,13 @@ import logging from homeassistant.components.vacuum import ( - VacuumDevice, SUPPORT_BATTERY, SUPPORT_RETURN_HOME, SUPPORT_CLEAN_SPOT, - SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, - SUPPORT_LOCATE, SUPPORT_FAN_SPEED, SUPPORT_SEND_COMMAND, ) -from homeassistant.components.ecovacs import ( - ECOVACS_DEVICES) + SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, + SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, + SUPPORT_TURN_OFF, SUPPORT_TURN_ON, VacuumDevice) from homeassistant.helpers.icon import icon_for_battery_level +from . import ECOVACS_DEVICES + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ecovacs'] diff --git a/homeassistant/components/edp_redy/sensor.py b/homeassistant/components/edp_redy/sensor.py index 389ae77f35be0a..b8f9c031c298c9 100644 --- a/homeassistant/components/edp_redy/sensor.py +++ b/homeassistant/components/edp_redy/sensor.py @@ -1,10 +1,10 @@ """Support for EDP re:dy sensors.""" import logging -from homeassistant.helpers.entity import Entity from homeassistant.const import POWER_WATT +from homeassistant.helpers.entity import Entity -from homeassistant.components.edp_redy import EdpRedyDevice, EDP_REDY +from . import EDP_REDY, EdpRedyDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/edp_redy/switch.py b/homeassistant/components/edp_redy/switch.py index ad4ce8fe72867e..0c92f80ccf6366 100644 --- a/homeassistant/components/edp_redy/switch.py +++ b/homeassistant/components/edp_redy/switch.py @@ -1,9 +1,10 @@ """Support for EDP re:dy plugs/switches.""" import logging -from homeassistant.components.edp_redy import EdpRedyDevice, EDP_REDY from homeassistant.components.switch import SwitchDevice +from . import EDP_REDY, EdpRedyDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['edp_redy'] diff --git a/homeassistant/components/egardia/alarm_control_panel.py b/homeassistant/components/egardia/alarm_control_panel.py index e202a46f9f15de..7fc60d5fb5db6e 100644 --- a/homeassistant/components/egardia/alarm_control_panel.py +++ b/homeassistant/components/egardia/alarm_control_panel.py @@ -4,14 +4,15 @@ import requests import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.egardia import ( - CONF_REPORT_SERVER_CODES, CONF_REPORT_SERVER_ENABLED, - CONF_REPORT_SERVER_PORT, EGARDIA_DEVICE, EGARDIA_SERVER, - REPORT_SERVER_CODES_IGNORE) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) +from . import ( + CONF_REPORT_SERVER_CODES, CONF_REPORT_SERVER_ENABLED, + CONF_REPORT_SERVER_PORT, EGARDIA_DEVICE, EGARDIA_SERVER, + REPORT_SERVER_CODES_IGNORE) + DEPENDENCIES = ['egardia'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/egardia/binary_sensor.py b/homeassistant/components/egardia/binary_sensor.py index 74a048a86c0f65..d11894ae675666 100644 --- a/homeassistant/components/egardia/binary_sensor.py +++ b/homeassistant/components/egardia/binary_sensor.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.egardia import ( - ATTR_DISCOVER_DEVICES, EGARDIA_DEVICE) from homeassistant.const import STATE_OFF, STATE_ON +from . import ATTR_DISCOVER_DEVICES, EGARDIA_DEVICE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['egardia'] diff --git a/homeassistant/components/eight_sleep/binary_sensor.py b/homeassistant/components/eight_sleep/binary_sensor.py index 2a9cb19a327a3a..a3ca27b570de0c 100644 --- a/homeassistant/components/eight_sleep/binary_sensor.py +++ b/homeassistant/components/eight_sleep/binary_sensor.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.eight_sleep import ( - DATA_EIGHT, EightSleepHeatEntity, CONF_BINARY_SENSORS, NAME_MAP) + +from . import CONF_BINARY_SENSORS, DATA_EIGHT, NAME_MAP, EightSleepHeatEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index 2bb03c8d4f2098..a1ad93ec54a3f3 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -1,9 +1,9 @@ """Support for Eight Sleep sensors.""" import logging -from homeassistant.components.eight_sleep import ( - DATA_EIGHT, EightSleepHeatEntity, EightSleepUserEntity, - CONF_SENSORS, NAME_MAP) +from . import ( + CONF_SENSORS, DATA_EIGHT, NAME_MAP, EightSleepHeatEntity, + EightSleepUserEntity) DEPENDENCIES = ['eight_sleep'] diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index 63b38c1d321fac..e9155dd17b5bf7 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -1,16 +1,17 @@ """Each ElkM1 area will be created as a separate alarm_control_panel.""" import voluptuous as vol + import homeassistant.components.alarm_control_panel as alarm from homeassistant.const import ( ATTR_CODE, ATTR_ENTITY_ID, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMING, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED) -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, create_elk_entities, ElkEntity) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send) +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities + DEPENDENCIES = [ELK_DOMAIN] SIGNAL_ARM_ENTITY = 'elkm1_arm' diff --git a/homeassistant/components/elkm1/climate.py b/homeassistant/components/elkm1/climate.py index 72f93b5419c509..93e4aa66b23494 100644 --- a/homeassistant/components/elkm1/climate.py +++ b/homeassistant/components/elkm1/climate.py @@ -1,14 +1,13 @@ """Support for control of Elk-M1 connected thermostats.""" from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, STATE_AUTO, - STATE_COOL, STATE_FAN_ONLY, STATE_HEAT, STATE_IDLE, SUPPORT_AUX_HEAT, - SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE_HIGH, + ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, STATE_AUTO, STATE_COOL, + STATE_FAN_ONLY, STATE_HEAT, STATE_IDLE, SUPPORT_AUX_HEAT, SUPPORT_FAN_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities) -from homeassistant.const import ( - STATE_ON, PRECISION_WHOLE) +from homeassistant.const import PRECISION_WHOLE, STATE_ON + +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities DEPENDENCIES = [ELK_DOMAIN] diff --git a/homeassistant/components/elkm1/light.py b/homeassistant/components/elkm1/light.py index 3a282595d5854e..fe84ab3f251895 100644 --- a/homeassistant/components/elkm1/light.py +++ b/homeassistant/components/elkm1/light.py @@ -1,8 +1,8 @@ """Support for control of ElkM1 lighting (X10, UPB, etc).""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities) + +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities DEPENDENCIES = [ELK_DOMAIN] diff --git a/homeassistant/components/elkm1/scene.py b/homeassistant/components/elkm1/scene.py index c8583b1d8bfec8..1d08f4cf96d5f1 100644 --- a/homeassistant/components/elkm1/scene.py +++ b/homeassistant/components/elkm1/scene.py @@ -1,8 +1,8 @@ """Support for control of ElkM1 tasks ("macros").""" -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities) from homeassistant.components.scene import Scene +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities + DEPENDENCIES = [ELK_DOMAIN] diff --git a/homeassistant/components/elkm1/sensor.py b/homeassistant/components/elkm1/sensor.py index 63da6ea5376588..da27a3ac4b105d 100644 --- a/homeassistant/components/elkm1/sensor.py +++ b/homeassistant/components/elkm1/sensor.py @@ -1,6 +1,5 @@ """Support for control of ElkM1 sensors.""" -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, create_elk_entities, ElkEntity) +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities DEPENDENCIES = [ELK_DOMAIN] diff --git a/homeassistant/components/elkm1/switch.py b/homeassistant/components/elkm1/switch.py index 7badd6ee5dc2ff..740a296586502f 100644 --- a/homeassistant/components/elkm1/switch.py +++ b/homeassistant/components/elkm1/switch.py @@ -1,8 +1,8 @@ """Support for control of ElkM1 outputs (relays).""" -from homeassistant.components.elkm1 import ( - DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities) from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities + DEPENDENCIES = [ELK_DOMAIN] diff --git a/homeassistant/components/emulated_roku/binding.py b/homeassistant/components/emulated_roku/binding.py index cd42560288d2bd..9dfb58a5e34852 100644 --- a/homeassistant/components/emulated_roku/binding.py +++ b/homeassistant/components/emulated_roku/binding.py @@ -5,7 +5,7 @@ EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import CoreState, EventOrigin -LOGGER = logging.getLogger('homeassistant.components.emulated_roku') +LOGGER = logging.getLogger('.') EVENT_ROKU_COMMAND = 'roku_command' diff --git a/homeassistant/components/envisalink/alarm_control_panel.py b/homeassistant/components/envisalink/alarm_control_panel.py index a4cc5864fc4c5b..44874c6d5e8445 100644 --- a/homeassistant/components/envisalink/alarm_control_panel.py +++ b/homeassistant/components/envisalink/alarm_control_panel.py @@ -3,16 +3,18 @@ import voluptuous as vol -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.components.alarm_control_panel as alarm -import homeassistant.helpers.config_validation as cv -from homeassistant.components.envisalink import ( - DATA_EVL, EnvisalinkDevice, PARTITION_SCHEMA, CONF_CODE, CONF_PANIC, - CONF_PARTITIONNAME, SIGNAL_KEYPAD_UPDATE, SIGNAL_PARTITION_UPDATE) from homeassistant.const import ( - STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, - STATE_UNKNOWN, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, ATTR_ENTITY_ID) + ATTR_ENTITY_ID, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, + STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, + STATE_UNKNOWN) +from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( + CONF_CODE, CONF_PANIC, CONF_PARTITIONNAME, DATA_EVL, PARTITION_SCHEMA, + SIGNAL_KEYPAD_UPDATE, SIGNAL_PARTITION_UPDATE, EnvisalinkDevice) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/envisalink/binary_sensor.py b/homeassistant/components/envisalink/binary_sensor.py index 26b54e16cc8c2e..267bba8cd288ee 100644 --- a/homeassistant/components/envisalink/binary_sensor.py +++ b/homeassistant/components/envisalink/binary_sensor.py @@ -1,16 +1,17 @@ """Support for Envisalink zone states- represented as binary sensors.""" -import logging import datetime +import logging -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.envisalink import ( - DATA_EVL, ZONE_SCHEMA, CONF_ZONENAME, CONF_ZONETYPE, EnvisalinkDevice, - SIGNAL_ZONE_UPDATE) from homeassistant.const import ATTR_LAST_TRIP_TIME +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import dt as dt_util +from . import ( + CONF_ZONENAME, CONF_ZONETYPE, DATA_EVL, SIGNAL_ZONE_UPDATE, ZONE_SCHEMA, + EnvisalinkDevice) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['envisalink'] diff --git a/homeassistant/components/envisalink/sensor.py b/homeassistant/components/envisalink/sensor.py index cc6a8b8723298e..67a601b02a2ff5 100644 --- a/homeassistant/components/envisalink/sensor.py +++ b/homeassistant/components/envisalink/sensor.py @@ -3,11 +3,12 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.components.envisalink import ( - DATA_EVL, PARTITION_SCHEMA, CONF_PARTITIONNAME, EnvisalinkDevice, - SIGNAL_KEYPAD_UPDATE, SIGNAL_PARTITION_UPDATE) from homeassistant.helpers.entity import Entity +from . import ( + CONF_PARTITIONNAME, DATA_EVL, PARTITION_SCHEMA, SIGNAL_KEYPAD_UPDATE, + SIGNAL_PARTITION_UPDATE, EnvisalinkDevice) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['envisalink'] diff --git a/homeassistant/components/esphome/binary_sensor.py b/homeassistant/components/esphome/binary_sensor.py index 4796df636baac4..2db2f209fa5ac9 100644 --- a/homeassistant/components/esphome/binary_sensor.py +++ b/homeassistant/components/esphome/binary_sensor.py @@ -1,11 +1,10 @@ """Support for ESPHome binary sensors.""" import logging - from typing import TYPE_CHECKING, Optional from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry + +from . import EsphomeEntity, platform_async_setup_entry if TYPE_CHECKING: # pylint: disable=unused-import diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index 14fce3fb4ebed5..d86c40e627e354 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -1,15 +1,14 @@ """Support for ESPHome covers.""" import logging - from typing import TYPE_CHECKING, Optional -from homeassistant.components.cover import CoverDevice, SUPPORT_CLOSE, \ - SUPPORT_OPEN, SUPPORT_STOP -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry +from homeassistant.components.cover import ( + SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, CoverDevice) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType +from . import EsphomeEntity, platform_async_setup_entry + if TYPE_CHECKING: # pylint: disable=unused-import from aioesphomeapi import CoverInfo, CoverState # noqa diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index 49e2401545be0f..05f18cb014acb2 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -1,14 +1,15 @@ """Support for ESPHome fans.""" import logging -from typing import List, Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, List, Optional -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry -from homeassistant.components.fan import FanEntity, SPEED_HIGH, SPEED_LOW, \ - SPEED_MEDIUM, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, SPEED_OFF +from homeassistant.components.fan import ( + SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_OSCILLATE, + SUPPORT_SET_SPEED, FanEntity) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType +from . import EsphomeEntity, platform_async_setup_entry + if TYPE_CHECKING: # pylint: disable=unused-import from aioesphomeapi import FanInfo, FanState # noqa diff --git a/homeassistant/components/esphome/light.py b/homeassistant/components/esphome/light.py index 3b0889ede8ed8d..c84c50010d9482 100644 --- a/homeassistant/components/esphome/light.py +++ b/homeassistant/components/esphome/light.py @@ -1,18 +1,18 @@ """Support for ESPHome lights.""" import logging -from typing import Optional, List, Tuple, TYPE_CHECKING - -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry -from homeassistant.components.light import Light, SUPPORT_FLASH, \ - SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION, SUPPORT_COLOR, \ - SUPPORT_WHITE_VALUE, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, ATTR_HS_COLOR, \ - ATTR_FLASH, ATTR_TRANSITION, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \ - ATTR_EFFECT, ATTR_WHITE_VALUE, FLASH_SHORT, FLASH_LONG +from typing import TYPE_CHECKING, List, Optional, Tuple + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_HS_COLOR, + ATTR_TRANSITION, ATTR_WHITE_VALUE, FLASH_LONG, FLASH_SHORT, + SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, + SUPPORT_FLASH, SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, Light) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType import homeassistant.util.color as color_util +from . import EsphomeEntity, platform_async_setup_entry + if TYPE_CHECKING: # pylint: disable=unused-import from aioesphomeapi import LightInfo, LightState # noqa diff --git a/homeassistant/components/esphome/sensor.py b/homeassistant/components/esphome/sensor.py index f776ea4683bf49..e4fb7ef82baf85 100644 --- a/homeassistant/components/esphome/sensor.py +++ b/homeassistant/components/esphome/sensor.py @@ -1,13 +1,13 @@ """Support for esphome sensors.""" import logging import math -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, Optional -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType +from . import EsphomeEntity, platform_async_setup_entry + if TYPE_CHECKING: # pylint: disable=unused-import from aioesphomeapi import ( # noqa diff --git a/homeassistant/components/esphome/switch.py b/homeassistant/components/esphome/switch.py index 4bf4b416b8df63..e5a9d0cf4461f2 100644 --- a/homeassistant/components/esphome/switch.py +++ b/homeassistant/components/esphome/switch.py @@ -1,14 +1,13 @@ """Support for ESPHome switches.""" import logging - from typing import TYPE_CHECKING, Optional -from homeassistant.components.esphome import EsphomeEntity, \ - platform_async_setup_entry from homeassistant.components.switch import SwitchDevice from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType +from . import EsphomeEntity, platform_async_setup_entry + if TYPE_CHECKING: # pylint: disable=unused-import from aioesphomeapi import SwitchInfo, SwitchState # noqa diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index 955b82e37e3c78..eea34e070012a4 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -6,30 +6,18 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, STATE_ECO, STATE_MANUAL, - SUPPORT_AWAY_MODE, - SUPPORT_ON_OFF, - SUPPORT_OPERATION_MODE, - SUPPORT_TARGET_TEMPERATURE, -) -from homeassistant.components.evohome import ( - DATA_EVOHOME, DISPATCHER_EVOHOME, - CONF_LOCATION_IDX, SCAN_INTERVAL_DEFAULT, - EVO_PARENT, EVO_CHILD, - GWS, TCS, -) + STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_AWAY_MODE, SUPPORT_ON_OFF, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - CONF_SCAN_INTERVAL, - HTTP_TOO_MANY_REQUESTS, - PRECISION_HALVES, - STATE_OFF, - TEMP_CELSIUS -) + CONF_SCAN_INTERVAL, HTTP_TOO_MANY_REQUESTS, PRECISION_HALVES, STATE_OFF, + TEMP_CELSIUS) from homeassistant.core import callback from homeassistant.helpers.dispatcher import ( - dispatcher_send, - async_dispatcher_connect -) + async_dispatcher_connect, dispatcher_send) + +from . import ( + CONF_LOCATION_IDX, DATA_EVOHOME, DISPATCHER_EVOHOME, EVO_CHILD, EVO_PARENT, + GWS, SCAN_INTERVAL_DEFAULT, TCS) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fan/demo.py b/homeassistant/components/fan/demo.py index 840196c8bf0e96..e67fbef650e6c7 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/fan/demo.py @@ -4,11 +4,12 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.fan import (SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, - FanEntity, SUPPORT_SET_SPEED, - SUPPORT_OSCILLATE, SUPPORT_DIRECTION) from homeassistant.const import STATE_OFF +from . import ( + SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, + SUPPORT_SET_SPEED, FanEntity) + FULL_SUPPORT = SUPPORT_SET_SPEED | SUPPORT_OSCILLATE | SUPPORT_DIRECTION LIMITED_SUPPORT = SUPPORT_SET_SPEED diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py index 0f17179f9185be..37fc0815ddcd93 100644 --- a/homeassistant/components/fastdotcom/sensor.py +++ b/homeassistant/components/fastdotcom/sensor.py @@ -1,12 +1,12 @@ """Support for Fast.com internet speed testing sensor.""" import logging -from homeassistant.components.fastdotcom import DOMAIN as FASTDOTCOM_DOMAIN, \ - DATA_UPDATED from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity +from . import DATA_UPDATED, DOMAIN as FASTDOTCOM_DOMAIN + DEPENDENCIES = ['fastdotcom'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index db9e73f3e1bcac..4272a3d6029dd9 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -9,13 +9,12 @@ import voluptuous as vol +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.const import CONF_NAME -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA -from homeassistant.components.ffmpeg import ( - DATA_FFMPEG, CONF_INPUT, CONF_EXTRA_ARGUMENTS) +from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.aiohttp_client import ( - async_aiohttp_proxy_stream) + +from . import CONF_EXTRA_ARGUMENTS, CONF_INPUT, DATA_FFMPEG _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fibaro/binary_sensor.py b/homeassistant/components/fibaro/binary_sensor.py index 2c2d9c30a794a6..f71a5f3662e8e2 100644 --- a/homeassistant/components/fibaro/binary_sensor.py +++ b/homeassistant/components/fibaro/binary_sensor.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.binary_sensor import ( - BinarySensorDevice, ENTITY_ID_FORMAT) -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice) -from homeassistant.const import (CONF_DEVICE_CLASS, CONF_ICON) + ENTITY_ID_FORMAT, BinarySensorDevice) +from homeassistant.const import CONF_DEVICE_CLASS, CONF_ICON + +from . import FIBARO_DEVICES, FibaroDevice DEPENDENCIES = ['fibaro'] diff --git a/homeassistant/components/fibaro/cover.py b/homeassistant/components/fibaro/cover.py index aa34fcc36a9658..0f5cc32bc9695b 100644 --- a/homeassistant/components/fibaro/cover.py +++ b/homeassistant/components/fibaro/cover.py @@ -2,9 +2,9 @@ import logging from homeassistant.components.cover import ( - CoverDevice, ENTITY_ID_FORMAT, ATTR_POSITION, ATTR_TILT_POSITION) -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice) + ATTR_POSITION, ATTR_TILT_POSITION, ENTITY_ID_FORMAT, CoverDevice) + +from . import FIBARO_DEVICES, FibaroDevice DEPENDENCIES = ['fibaro'] diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index 5ee3e83b95fd72..600b566b36b23c 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -1,18 +1,17 @@ """Support for Fibaro lights.""" -import logging import asyncio from functools import partial +import logging -from homeassistant.const import ( - CONF_WHITE_VALUE) -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice, - CONF_DIMMING, CONF_COLOR, CONF_RESET_COLOR) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_WHITE_VALUE, ENTITY_ID_FORMAT, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_WHITE_VALUE, Light) +from homeassistant.const import CONF_WHITE_VALUE import homeassistant.util.color as color_util +from . import ( + CONF_COLOR, CONF_DIMMING, CONF_RESET_COLOR, FIBARO_DEVICES, FibaroDevice) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['fibaro'] diff --git a/homeassistant/components/fibaro/scene.py b/homeassistant/components/fibaro/scene.py index 620f095b733a4b..93f0cd5b63afd0 100644 --- a/homeassistant/components/fibaro/scene.py +++ b/homeassistant/components/fibaro/scene.py @@ -1,10 +1,9 @@ """Support for Fibaro scenes.""" import logging -from homeassistant.components.scene import ( - Scene) -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice) +from homeassistant.components.scene import Scene + +from . import FIBARO_DEVICES, FibaroDevice DEPENDENCIES = ['fibaro'] diff --git a/homeassistant/components/fibaro/sensor.py b/homeassistant/components/fibaro/sensor.py index 01452d8b394a7e..20a37fd3c23756 100644 --- a/homeassistant/components/fibaro/sensor.py +++ b/homeassistant/components/fibaro/sensor.py @@ -1,13 +1,13 @@ """Support for Fibaro sensors.""" import logging +from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.const import ( - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_ILLUMINANCE, TEMP_CELSIUS, TEMP_FAHRENHEIT) + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, + TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import Entity -from homeassistant.components.sensor import ENTITY_ID_FORMAT -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice) + +from . import FIBARO_DEVICES, FibaroDevice SENSOR_TYPES = { 'com.fibaro.temperatureSensor': diff --git a/homeassistant/components/fibaro/switch.py b/homeassistant/components/fibaro/switch.py index 04b8aba1cf43a3..024531f62c70e8 100644 --- a/homeassistant/components/fibaro/switch.py +++ b/homeassistant/components/fibaro/switch.py @@ -1,10 +1,10 @@ """Support for Fibaro switches.""" import logging -from homeassistant.util import convert from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice -from homeassistant.components.fibaro import ( - FIBARO_DEVICES, FibaroDevice) +from homeassistant.util import convert + +from . import FIBARO_DEVICES, FibaroDevice DEPENDENCIES = ['fibaro'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/freebox/device_tracker.py b/homeassistant/components/freebox/device_tracker.py index fb94f7f56f5ff2..5418c1c61a7322 100644 --- a/homeassistant/components/freebox/device_tracker.py +++ b/homeassistant/components/freebox/device_tracker.py @@ -3,7 +3,8 @@ import logging from homeassistant.components.device_tracker import DeviceScanner -from homeassistant.components.freebox import DATA_FREEBOX + +from . import DATA_FREEBOX DEPENDENCIES = ['freebox'] diff --git a/homeassistant/components/freebox/sensor.py b/homeassistant/components/freebox/sensor.py index 49e68dc2c414df..328665ab51cd9b 100644 --- a/homeassistant/components/freebox/sensor.py +++ b/homeassistant/components/freebox/sensor.py @@ -1,9 +1,10 @@ """Support for Freebox devices (Freebox v6 and Freebox mini 4K).""" import logging -from homeassistant.components.freebox import DATA_FREEBOX from homeassistant.helpers.entity import Entity +from . import DATA_FREEBOX + DEPENDENCIES = ['freebox'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index c68c79f1e774b2..65578c571805e9 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -4,7 +4,8 @@ import requests from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.fritzbox import DOMAIN as FRITZBOX_DOMAIN + +from . import DOMAIN as FRITZBOX_DOMAIN DEPENDENCIES = ['fritzbox'] diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index e8c20061b4e237..e2c9be833ac4d3 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -3,19 +3,19 @@ import requests -from homeassistant.components.fritzbox import DOMAIN as FRITZBOX_DOMAIN -from homeassistant.components.fritzbox import ( - ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_BATTERY_LOW, ATTR_STATE_HOLIDAY_MODE, - ATTR_STATE_LOCKED, ATTR_STATE_SUMMER_MODE, - ATTR_STATE_WINDOW_OPEN) from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( ATTR_OPERATION_MODE, STATE_ECO, STATE_HEAT, STATE_MANUAL, - SUPPORT_OPERATION_MODE, - SUPPORT_TARGET_TEMPERATURE) + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE, PRECISION_HALVES, TEMP_CELSIUS, - STATE_OFF, STATE_ON) + ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE, PRECISION_HALVES, STATE_OFF, + STATE_ON, TEMP_CELSIUS) + +from . import ( + ATTR_STATE_BATTERY_LOW, ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_HOLIDAY_MODE, + ATTR_STATE_LOCKED, ATTR_STATE_SUMMER_MODE, ATTR_STATE_WINDOW_OPEN, + DOMAIN as FRITZBOX_DOMAIN) + DEPENDENCIES = ['fritzbox'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index a1736fb9857526..7309f8cc6180a6 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -3,11 +3,11 @@ import requests -from homeassistant.components.fritzbox import DOMAIN as FRITZBOX_DOMAIN -from homeassistant.components.fritzbox import ( - ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED) -from homeassistant.helpers.entity import Entity from homeassistant.const import TEMP_CELSIUS +from homeassistant.helpers.entity import Entity + +from . import ( + ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED, DOMAIN as FRITZBOX_DOMAIN) DEPENDENCIES = ['fritzbox'] diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py index 617c4902068fbb..e227cdaef8a00a 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -3,12 +3,12 @@ import requests -from homeassistant.components.fritzbox import DOMAIN as FRITZBOX_DOMAIN -from homeassistant.components.fritzbox import ( - ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED) from homeassistant.components.switch import SwitchDevice -from homeassistant.const import (ATTR_TEMPERATURE, TEMP_CELSIUS, - ENERGY_KILO_WATT_HOUR) +from homeassistant.const import ( + ATTR_TEMPERATURE, ENERGY_KILO_WATT_HOUR, TEMP_CELSIUS) + +from . import ( + ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED, DOMAIN as FRITZBOX_DOMAIN) DEPENDENCIES = ['fritzbox'] diff --git a/homeassistant/components/gc100/binary_sensor.py b/homeassistant/components/gc100/binary_sensor.py index 27466f64cfb727..ec69d1eec83421 100644 --- a/homeassistant/components/gc100/binary_sensor.py +++ b/homeassistant/components/gc100/binary_sensor.py @@ -6,12 +6,13 @@ """ import voluptuous as vol -from homeassistant.components.gc100 import DATA_GC100, CONF_PORTS from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import DEVICE_DEFAULT_NAME import homeassistant.helpers.config_validation as cv +from . import CONF_PORTS, DATA_GC100 + DEPENDENCIES = ['gc100'] _SENSORS_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/gc100/switch.py b/homeassistant/components/gc100/switch.py index 2a8e7eaa847cc1..94c824fa5d784d 100644 --- a/homeassistant/components/gc100/switch.py +++ b/homeassistant/components/gc100/switch.py @@ -6,11 +6,12 @@ """ import voluptuous as vol +from homeassistant.components.switch import PLATFORM_SCHEMA +from homeassistant.const import DEVICE_DEFAULT_NAME import homeassistant.helpers.config_validation as cv -from homeassistant.components.gc100 import DATA_GC100, CONF_PORTS -from homeassistant.components.switch import (PLATFORM_SCHEMA) from homeassistant.helpers.entity import ToggleEntity -from homeassistant.const import DEVICE_DEFAULT_NAME + +from . import CONF_PORTS, DATA_GC100 DEPENDENCIES = ['gc100'] diff --git a/homeassistant/components/geo_location/demo.py b/homeassistant/components/geo_location/demo.py index 523e125a737b8b..d63280ce609a7f 100644 --- a/homeassistant/components/geo_location/demo.py +++ b/homeassistant/components/geo_location/demo.py @@ -5,9 +5,10 @@ import random from typing import Optional -from homeassistant.components.geo_location import GeolocationEvent from homeassistant.helpers.event import track_time_interval +from . import GeolocationEvent + _LOGGER = logging.getLogger(__name__) AVG_KM_PER_DEGREE = 111.0 diff --git a/homeassistant/components/geofency/device_tracker.py b/homeassistant/components/geofency/device_tracker.py index 51201240c1c4ee..5e7b8291840fec 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -8,10 +8,10 @@ from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN) -from homeassistant.components.geofency import ( - DOMAIN as GEOFENCY_DOMAIN, TRACKER_UPDATE) from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DOMAIN as GEOFENCY_DOMAIN, TRACKER_UPDATE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['geofency'] diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index cc65c6d655d3ed..9f71e7c4f20367 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -1,14 +1,14 @@ """Support for Google Calendar Search binary sensors.""" -import logging from datetime import timedelta +import logging from homeassistant.components.calendar import CalendarEventDevice -from homeassistant.components.google import ( - CONF_CAL_ID, CONF_ENTITIES, CONF_TRACK, TOKEN_FILE, - CONF_IGNORE_AVAILABILITY, CONF_SEARCH, - GoogleCalendarService) from homeassistant.util import Throttle, dt +from . import ( + CONF_CAL_ID, CONF_ENTITIES, CONF_IGNORE_AVAILABILITY, CONF_SEARCH, + CONF_TRACK, TOKEN_FILE, GoogleCalendarService) + _LOGGER = logging.getLogger(__name__) DEFAULT_GOOGLE_SEARCH_PARAMS = { diff --git a/homeassistant/components/googlehome/device_tracker.py b/homeassistant/components/googlehome/device_tracker.py index 462f5db3b9b779..c024cde0c6ce66 100644 --- a/homeassistant/components/googlehome/device_tracker.py +++ b/homeassistant/components/googlehome/device_tracker.py @@ -1,13 +1,13 @@ """Support for Google Home Bluetooth tacker.""" -import logging from datetime import timedelta +import logging from homeassistant.components.device_tracker import DeviceScanner -from homeassistant.components.googlehome import ( - CLIENT, DOMAIN as GOOGLEHOME_DOMAIN, NAME) from homeassistant.helpers.event import async_track_time_interval from homeassistant.util import slugify +from . import CLIENT, DOMAIN as GOOGLEHOME_DOMAIN, NAME + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['googlehome'] diff --git a/homeassistant/components/googlehome/sensor.py b/homeassistant/components/googlehome/sensor.py index 7577ee0b01782a..4f37740da85ed2 100644 --- a/homeassistant/components/googlehome/sensor.py +++ b/homeassistant/components/googlehome/sensor.py @@ -1,13 +1,13 @@ """Support for Google Home alarm sensor.""" -import logging from datetime import timedelta +import logging -from homeassistant.components.googlehome import ( - CLIENT, DOMAIN as GOOGLEHOME_DOMAIN, NAME) from homeassistant.const import DEVICE_CLASS_TIMESTAMP from homeassistant.helpers.entity import Entity import homeassistant.util.dt as dt_util +from . import CLIENT, DOMAIN as GOOGLEHOME_DOMAIN, NAME + DEPENDENCIES = ['googlehome'] SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/gpslogger/device_tracker.py b/homeassistant/components/gpslogger/device_tracker.py index 90d2dc04f89125..c9496975272811 100644 --- a/homeassistant/components/gpslogger/device_tracker.py +++ b/homeassistant/components/gpslogger/device_tracker.py @@ -1,13 +1,13 @@ """Support for the GPSLogger device tracking.""" import logging -from homeassistant.components.device_tracker import DOMAIN as \ - DEVICE_TRACKER_DOMAIN -from homeassistant.components.gpslogger import DOMAIN as GPSLOGGER_DOMAIN, \ - TRACKER_UPDATE +from homeassistant.components.device_tracker import ( + DOMAIN as DEVICE_TRACKER_DOMAIN) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import HomeAssistantType +from . import DOMAIN as GPSLOGGER_DOMAIN, TRACKER_UPDATE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['gpslogger'] diff --git a/homeassistant/components/hangouts/__init__.py b/homeassistant/components/hangouts/__init__.py index 4796744c170b14..2d36de8b7692bf 100644 --- a/homeassistant/components/hangouts/__init__.py +++ b/homeassistant/components/hangouts/__init__.py @@ -4,12 +4,12 @@ import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.hangouts.intents import HelpIntent from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.helpers import dispatcher, intent import homeassistant.helpers.config_validation as cv # We need an import from .config_flow, without it .config_flow is never loaded. +from .intents import HelpIntent from .config_flow import HangoutsFlowHandler # noqa: F401 from .const import ( CONF_BOT, CONF_DEFAULT_CONVERSATIONS, CONF_ERROR_SUPPRESSED_CONVERSATIONS, diff --git a/homeassistant/components/hangouts/const.py b/homeassistant/components/hangouts/const.py index ca0fdf986eeca3..38b238292b3333 100644 --- a/homeassistant/components/hangouts/const.py +++ b/homeassistant/components/hangouts/const.py @@ -7,7 +7,7 @@ ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET) import homeassistant.helpers.config_validation as cv -_LOGGER = logging.getLogger('homeassistant.components.hangouts') +_LOGGER = logging.getLogger('.') DOMAIN = 'hangouts' diff --git a/homeassistant/components/hangouts/notify.py b/homeassistant/components/hangouts/notify.py index c3b5450be0574a..de9af2e077512b 100644 --- a/homeassistant/components/hangouts/notify.py +++ b/homeassistant/components/hangouts/notify.py @@ -3,12 +3,13 @@ import voluptuous as vol -from homeassistant.components.hangouts.const import ( - CONF_DEFAULT_CONVERSATIONS, DOMAIN, SERVICE_SEND_MESSAGE, TARGETS_SCHEMA) from homeassistant.components.notify import ( ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) +from .const import ( + CONF_DEFAULT_CONVERSATIONS, DOMAIN, SERVICE_SEND_MESSAGE, TARGETS_SCHEMA) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = [DOMAIN] diff --git a/homeassistant/components/hdmi_cec/media_player.py b/homeassistant/components/hdmi_cec/media_player.py index 553983a1f03fe4..b2d2910e145bbe 100644 --- a/homeassistant/components/hdmi_cec/media_player.py +++ b/homeassistant/components/hdmi_cec/media_player.py @@ -1,7 +1,6 @@ """Support for HDMI CEC devices as media players.""" import logging -from homeassistant.components.hdmi_cec import ATTR_NEW, CecDevice from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( DOMAIN, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, @@ -10,6 +9,8 @@ from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING) +from . import ATTR_NEW, CecDevice + DEPENDENCIES = ['hdmi_cec'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index ff423890ba5840..639f545707ee2b 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -1,10 +1,11 @@ """Support for HDMI CEC devices as switches.""" import logging -from homeassistant.components.hdmi_cec import ATTR_NEW, CecDevice from homeassistant.components.switch import DOMAIN, SwitchDevice from homeassistant.const import STATE_OFF, STATE_ON, STATE_STANDBY +from . import ATTR_NEW, CecDevice + DEPENDENCIES = ['hdmi_cec'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hive/binary_sensor.py b/homeassistant/components/hive/binary_sensor.py index dee27c5c7104d7..a0973f4d8e93a7 100644 --- a/homeassistant/components/hive/binary_sensor.py +++ b/homeassistant/components/hive/binary_sensor.py @@ -1,6 +1,7 @@ """Support for the Hive binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.hive import DATA_HIVE, DOMAIN + +from . import DATA_HIVE, DOMAIN DEPENDENCIES = ['hive'] diff --git a/homeassistant/components/hive/climate.py b/homeassistant/components/hive/climate.py index 45829cda087324..dac7feb2927fe6 100644 --- a/homeassistant/components/hive/climate.py +++ b/homeassistant/components/hive/climate.py @@ -1,12 +1,13 @@ """Support for the Hive climate devices.""" from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, STATE_HEAT, - SUPPORT_AUX_HEAT, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) -from homeassistant.components.hive import DATA_HIVE, DOMAIN + STATE_AUTO, STATE_HEAT, SUPPORT_AUX_HEAT, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS) +from . import DATA_HIVE, DOMAIN + DEPENDENCIES = ['hive'] HIVE_TO_HASS_STATE = { diff --git a/homeassistant/components/hive/light.py b/homeassistant/components/hive/light.py index 2bec60f0ee4224..3a2176c3eedab7 100644 --- a/homeassistant/components/hive/light.py +++ b/homeassistant/components/hive/light.py @@ -1,10 +1,11 @@ """Support for the Hive lights.""" -from homeassistant.components.hive import DATA_HIVE, DOMAIN from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) import homeassistant.util.color as color_util +from . import DATA_HIVE, DOMAIN + DEPENDENCIES = ['hive'] diff --git a/homeassistant/components/hive/sensor.py b/homeassistant/components/hive/sensor.py index 142c8c7ee94f72..e7b7d6b45977aa 100644 --- a/homeassistant/components/hive/sensor.py +++ b/homeassistant/components/hive/sensor.py @@ -1,8 +1,9 @@ """Support for the Hive sensors.""" -from homeassistant.components.hive import DATA_HIVE, DOMAIN from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import DATA_HIVE, DOMAIN + DEPENDENCIES = ['hive'] FRIENDLY_NAMES = { diff --git a/homeassistant/components/hive/switch.py b/homeassistant/components/hive/switch.py index c897e37f34bcaa..fd4d3d69b50a06 100644 --- a/homeassistant/components/hive/switch.py +++ b/homeassistant/components/hive/switch.py @@ -1,7 +1,8 @@ """Support for the Hive switches.""" -from homeassistant.components.hive import DATA_HIVE, DOMAIN from homeassistant.components.switch import SwitchDevice +from . import DATA_HIVE, DOMAIN + DEPENDENCIES = ['hive'] diff --git a/homeassistant/components/hlk_sw16/switch.py b/homeassistant/components/hlk_sw16/switch.py index b1bfc5ce23d006..164a504fa34406 100644 --- a/homeassistant/components/hlk_sw16/switch.py +++ b/homeassistant/components/hlk_sw16/switch.py @@ -1,13 +1,11 @@ """Support for HLK-SW16 switches.""" import logging -from homeassistant.components.hlk_sw16 import ( - SW16Device, DOMAIN as HLK_SW16, - DATA_DEVICE_REGISTER) -from homeassistant.components.switch import ( - ToggleEntity) +from homeassistant.components.switch import ToggleEntity from homeassistant.const import CONF_NAME +from . import DATA_DEVICE_REGISTER, DOMAIN as HLK_SW16, SW16Device + DEPENDENCIES = [HLK_SW16] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/alarm_control_panel.py b/homeassistant/components/homekit_controller/alarm_control_panel.py index 61352c3bedc223..9bc15aad75dcb0 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -2,12 +2,12 @@ import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) +from . import KNOWN_ACCESSORIES, HomeKitEntity + DEPENDENCIES = ['homekit_controller'] ICON = 'mdi:security' diff --git a/homeassistant/components/homekit_controller/binary_sensor.py b/homeassistant/components/homekit_controller/binary_sensor.py index 5d83ce6d984ea6..7fcc5b4e833231 100644 --- a/homeassistant/components/homekit_controller/binary_sensor.py +++ b/homeassistant/components/homekit_controller/binary_sensor.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) + +from . import KNOWN_ACCESSORIES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 8696d2b1f97733..243b795e792fd9 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -3,11 +3,11 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_HEAT, STATE_COOL, STATE_IDLE, - SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) -from homeassistant.components.homekit_controller import ( - HomeKitEntity, KNOWN_ACCESSORIES) -from homeassistant.const import TEMP_CELSIUS, STATE_OFF, ATTR_TEMPERATURE + STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS + +from . import KNOWN_ACCESSORIES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index 4cd4c9ed251aca..4db1246b992ffe 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -5,11 +5,11 @@ ATTR_POSITION, ATTR_TILT_POSITION, SUPPORT_CLOSE, SUPPORT_CLOSE_TILT, SUPPORT_OPEN, SUPPORT_OPEN_TILT, SUPPORT_SET_POSITION, SUPPORT_SET_TILT_POSITION, CoverDevice) -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.const import ( STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING) +from . import KNOWN_ACCESSORIES, HomeKitEntity + STATE_STOPPED = 'stopped' DEPENDENCIES = ['homekit_controller'] diff --git a/homeassistant/components/homekit_controller/light.py b/homeassistant/components/homekit_controller/light.py index b5677c0e095788..db8fd332c0cf97 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -1,12 +1,12 @@ """Support for Homekit lights.""" import logging -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) +from . import KNOWN_ACCESSORIES, HomeKitEntity + DEPENDENCIES = ['homekit_controller'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index 6da5fa356558b7..a2aac5767bdf61 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -1,12 +1,12 @@ """Support for HomeKit Controller locks.""" import logging -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.components.lock import LockDevice from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_LOCKED, STATE_UNLOCKED) +from . import KNOWN_ACCESSORIES, HomeKitEntity + DEPENDENCIES = ['homekit_controller'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 5af0016eb16d8c..955a1a7927e0e5 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -1,8 +1,8 @@ """Support for Homekit sensors.""" -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.const import TEMP_CELSIUS +from . import KNOWN_ACCESSORIES, HomeKitEntity + DEPENDENCIES = ['homekit_controller'] HUMIDITY_ICON = 'mdi-water-percent' diff --git a/homeassistant/components/homekit_controller/switch.py b/homeassistant/components/homekit_controller/switch.py index 21f10e6243c001..ba19413d4115c6 100644 --- a/homeassistant/components/homekit_controller/switch.py +++ b/homeassistant/components/homekit_controller/switch.py @@ -1,10 +1,10 @@ """Support for Homekit switches.""" import logging -from homeassistant.components.homekit_controller import ( - KNOWN_ACCESSORIES, HomeKitEntity) from homeassistant.components.switch import SwitchDevice +from . import KNOWN_ACCESSORIES, HomeKitEntity + DEPENDENCIES = ['homekit_controller'] OUTLET_IN_USE = "outlet_in_use" diff --git a/homeassistant/components/homematic/binary_sensor.py b/homeassistant/components/homematic/binary_sensor.py index 1704411c9cc6d9..7bf260a9bdc385 100644 --- a/homeassistant/components/homematic/binary_sensor.py +++ b/homeassistant/components/homematic/binary_sensor.py @@ -2,9 +2,10 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.const import STATE_UNKNOWN +from . import ATTR_DISCOVER_DEVICES, HMDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] diff --git a/homeassistant/components/homematic/climate.py b/homeassistant/components/homematic/climate.py index e5eb292b4ff9d2..146cad1bc4ca75 100644 --- a/homeassistant/components/homematic/climate.py +++ b/homeassistant/components/homematic/climate.py @@ -5,10 +5,10 @@ from homeassistant.components.climate.const import ( STATE_AUTO, STATE_MANUAL, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.homematic import ( - ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT, HMDevice) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from . import ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT, HMDevice + DEPENDENCIES = ['homematic'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematic/cover.py b/homeassistant/components/homematic/cover.py index 79a1afe9a0e396..33b764dc31fa21 100644 --- a/homeassistant/components/homematic/cover.py +++ b/homeassistant/components/homematic/cover.py @@ -3,9 +3,10 @@ from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, CoverDevice) -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.const import STATE_UNKNOWN +from . import ATTR_DISCOVER_DEVICES, HMDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] diff --git a/homeassistant/components/homematic/light.py b/homeassistant/components/homematic/light.py index 21b875742c4f38..c3601461173783 100644 --- a/homeassistant/components/homematic/light.py +++ b/homeassistant/components/homematic/light.py @@ -1,11 +1,12 @@ """Support for Homematic lights.""" import logging -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_EFFECT, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_EFFECT, Light) +from . import ATTR_DISCOVER_DEVICES, HMDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] diff --git a/homeassistant/components/homematic/lock.py b/homeassistant/components/homematic/lock.py index 5d857617fdef66..3c0ca040c5f522 100644 --- a/homeassistant/components/homematic/lock.py +++ b/homeassistant/components/homematic/lock.py @@ -1,10 +1,11 @@ """Support for Homematic locks.""" import logging -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.components.lock import SUPPORT_OPEN, LockDevice from homeassistant.const import STATE_UNKNOWN +from . import ATTR_DISCOVER_DEVICES, HMDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] diff --git a/homeassistant/components/homematic/notify.py b/homeassistant/components/homematic/notify.py index e6ef1a60e287cb..021560eee3cbb1 100644 --- a/homeassistant/components/homematic/notify.py +++ b/homeassistant/components/homematic/notify.py @@ -8,14 +8,15 @@ import voluptuous as vol -from homeassistant.components.homematic import ( - ATTR_ADDRESS, ATTR_CHANNEL, ATTR_INTERFACE, ATTR_PARAM, ATTR_VALUE, DOMAIN, - SERVICE_SET_DEVICE_VALUE) from homeassistant.components.notify import ( ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv import homeassistant.helpers.template as template_helper +from . import ( + ATTR_ADDRESS, ATTR_CHANNEL, ATTR_INTERFACE, ATTR_PARAM, ATTR_VALUE, DOMAIN, + SERVICE_SET_DEVICE_VALUE) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ["homematic"] diff --git a/homeassistant/components/homematic/sensor.py b/homeassistant/components/homematic/sensor.py index 8e3e55e1f7f1e9..401d11f70c849b 100644 --- a/homeassistant/components/homematic/sensor.py +++ b/homeassistant/components/homematic/sensor.py @@ -1,8 +1,9 @@ """Support for HomeMatic sensors.""" import logging -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice -from homeassistant.const import STATE_UNKNOWN, POWER_WATT, ENERGY_WATT_HOUR +from homeassistant.const import ENERGY_WATT_HOUR, POWER_WATT, STATE_UNKNOWN + +from . import ATTR_DISCOVER_DEVICES, HMDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematic/switch.py b/homeassistant/components/homematic/switch.py index cfcd26891e018e..393ad09b3104ef 100644 --- a/homeassistant/components/homematic/switch.py +++ b/homeassistant/components/homematic/switch.py @@ -1,10 +1,11 @@ """Support for HomeMatic switches.""" import logging -from homeassistant.components.homematic import ATTR_DISCOVER_DEVICES, HMDevice from homeassistant.components.switch import SwitchDevice from homeassistant.const import STATE_UNKNOWN +from . import ATTR_DISCOVER_DEVICES, HMDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematic'] diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index efa1ea1f46e2cd..eb5855bb98099e 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -2,12 +2,12 @@ import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematicip_cloud'] diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 9445d6521cc42b..786a28a70a5fd3 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -2,10 +2,9 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) -from homeassistant.components.homematicip_cloud.device import ( - ATTR_GROUP_MEMBER_UNREACHABLE) + +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from .device import ATTR_GROUP_MEMBER_UNREACHABLE DEPENDENCIES = ['homematicip_cloud'] diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index 08c88bbb796bc7..955f3e5baa7342 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -4,10 +4,10 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( STATE_AUTO, STATE_MANUAL, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice + _LOGGER = logging.getLogger(__name__) STATE_BOOST = 'Boost' diff --git a/homeassistant/components/homematicip_cloud/const.py b/homeassistant/components/homematicip_cloud/const.py index fbda56f280549e..c9a5df601e4d2e 100644 --- a/homeassistant/components/homematicip_cloud/const.py +++ b/homeassistant/components/homematicip_cloud/const.py @@ -1,7 +1,7 @@ """Constants for the HomematicIP Cloud component.""" import logging -_LOGGER = logging.getLogger('homeassistant.components.homematicip_cloud') +_LOGGER = logging.getLogger('.') DOMAIN = 'homematicip_cloud' diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 86c11dab70dd33..735e8789670452 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.cover import ATTR_POSITION, CoverDevice -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) + +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice DEPENDENCIES = ['homematicip_cloud'] diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index 73c607683ba6b7..f8b19b5bb1edbc 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -1,12 +1,12 @@ """Support for HomematicIP Cloud lights.""" import logging -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_NAME, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice + DEPENDENCIES = ['homematicip_cloud'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index d6155998332662..39758739400291 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -1,12 +1,12 @@ """Support for HomematicIP Cloud sensors.""" import logging -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) from homeassistant.const import ( DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, POWER_WATT, TEMP_CELSIUS) +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['homematicip_cloud'] diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index 74f50f87b25981..62e72f0ade7b5a 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -1,12 +1,11 @@ """Support for HomematicIP Cloud switches.""" import logging -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) -from homeassistant.components.homematicip_cloud.device import ( - ATTR_GROUP_MEMBER_UNREACHABLE) from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from .device import ATTR_GROUP_MEMBER_UNREACHABLE + DEPENDENCIES = ['homematicip_cloud'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index 5a6261195da4bd..101adcdeaaa478 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -2,10 +2,10 @@ """Support for HomematicIP Cloud weather devices.""" import logging -from homeassistant.components.homematicip_cloud import ( - DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) from homeassistant.components.weather import WeatherEntity +from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice + DEPENDENCIES = ['homematicip_cloud'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homeworks/light.py b/homeassistant/components/homeworks/light.py index 7f5d7f6aab7e0e..ca41dff9834b44 100644 --- a/homeassistant/components/homeworks/light.py +++ b/homeassistant/components/homeworks/light.py @@ -1,15 +1,16 @@ """Support for Lutron Homeworks lights.""" import logging -from homeassistant.components.homeworks import ( - CONF_ADDR, CONF_DIMMERS, CONF_RATE, ENTITY_SIGNAL, HOMEWORKS_CONTROLLER, - HomeworksDevice) from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) from homeassistant.const import CONF_NAME from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import ( + CONF_ADDR, CONF_DIMMERS, CONF_RATE, ENTITY_SIGNAL, HOMEWORKS_CONTROLLER, + HomeworksDevice) + DEPENDENCIES = ['homeworks'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index bb7f2c2fee2f47..daac9fef74823e 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -9,12 +9,12 @@ import voluptuous as vol from homeassistant import exceptions -from homeassistant.components.http.ban import process_success_login from homeassistant.const import CONTENT_TYPE_JSON from homeassistant.core import Context, is_callback from homeassistant.helpers.json import JSONEncoder -from .const import KEY_AUTHENTICATED, KEY_REAL_IP, KEY_HASS +from .ban import process_success_login +from .const import KEY_AUTHENTICATED, KEY_HASS, KEY_REAL_IP _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hue/const.py b/homeassistant/components/hue/const.py index 2eb30d478044ec..d61a0aa7e89a95 100644 --- a/homeassistant/components/hue/const.py +++ b/homeassistant/components/hue/const.py @@ -1,6 +1,6 @@ """Constants for the Hue component.""" import logging -LOGGER = logging.getLogger('homeassistant.components.hue') +LOGGER = logging.getLogger('.') DOMAIN = "hue" API_NUPNP = 'https://www.meethue.com/api/nupnp' diff --git a/homeassistant/components/hydrawise/binary_sensor.py b/homeassistant/components/hydrawise/binary_sensor.py index bfe7cbd5531ed5..85a51d3649eb55 100644 --- a/homeassistant/components/hydrawise/binary_sensor.py +++ b/homeassistant/components/hydrawise/binary_sensor.py @@ -3,13 +3,14 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.hydrawise import ( - BINARY_SENSORS, DATA_HYDRAWISE, HydrawiseEntity, DEVICE_MAP, - DEVICE_MAP_INDEX) from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv + +from . import ( + BINARY_SENSORS, DATA_HYDRAWISE, DEVICE_MAP, DEVICE_MAP_INDEX, + HydrawiseEntity) DEPENDENCIES = ['hydrawise'] diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index 575686b92cdd59..fc15a54ed60963 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -3,11 +3,12 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.hydrawise import ( - DATA_HYDRAWISE, HydrawiseEntity, DEVICE_MAP, DEVICE_MAP_INDEX, SENSORS) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv + +from . import ( + DATA_HYDRAWISE, DEVICE_MAP, DEVICE_MAP_INDEX, SENSORS, HydrawiseEntity) DEPENDENCIES = ['hydrawise'] diff --git a/homeassistant/components/hydrawise/switch.py b/homeassistant/components/hydrawise/switch.py index a6a8b9c54cff5f..dcbd5274a6237d 100644 --- a/homeassistant/components/hydrawise/switch.py +++ b/homeassistant/components/hydrawise/switch.py @@ -3,13 +3,14 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.hydrawise import ( - ALLOWED_WATERING_TIME, CONF_WATERING_TIME, - DATA_HYDRAWISE, DEFAULT_WATERING_TIME, HydrawiseEntity, SWITCHES, - DEVICE_MAP, DEVICE_MAP_INDEX) from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv + +from . import ( + ALLOWED_WATERING_TIME, CONF_WATERING_TIME, DATA_HYDRAWISE, + DEFAULT_WATERING_TIME, DEVICE_MAP, DEVICE_MAP_INDEX, SWITCHES, + HydrawiseEntity) DEPENDENCIES = ['hydrawise'] diff --git a/homeassistant/components/ifttt/alarm_control_panel.py b/homeassistant/components/ifttt/alarm_control_panel.py index 98a176b1e82459..3f806173196897 100644 --- a/homeassistant/components/ifttt/alarm_control_panel.py +++ b/homeassistant/components/ifttt/alarm_control_panel.py @@ -7,14 +7,14 @@ import homeassistant.components.alarm_control_panel as alarm from homeassistant.components.alarm_control_panel import ( DOMAIN, PLATFORM_SCHEMA) -from homeassistant.components.ifttt import ( - ATTR_EVENT, DOMAIN as IFTTT_DOMAIN, SERVICE_TRIGGER) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_STATE, CONF_CODE, CONF_NAME, CONF_OPTIMISTIC, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED) import homeassistant.helpers.config_validation as cv +from . import ATTR_EVENT, DOMAIN as IFTTT_DOMAIN, SERVICE_TRIGGER + DEPENDENCIES = ['ifttt'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ihc/__init__.py b/homeassistant/components/ihc/__init__.py index daaf471e3182d9..102acd82551c4f 100644 --- a/homeassistant/components/ihc/__init__.py +++ b/homeassistant/components/ihc/__init__.py @@ -5,13 +5,6 @@ import voluptuous as vol from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA -from homeassistant.components.ihc.const import ( - ATTR_IHC_ID, ATTR_VALUE, CONF_AUTOSETUP, CONF_BINARY_SENSOR, CONF_DIMMABLE, - CONF_INFO, CONF_INVERTING, CONF_LIGHT, CONF_NODE, CONF_NOTE, CONF_OFF_ID, - CONF_ON_ID, CONF_POSITION, CONF_SENSOR, CONF_SWITCH, CONF_XPATH, - SERVICE_SET_RUNTIME_VALUE_BOOL, SERVICE_SET_RUNTIME_VALUE_FLOAT, - SERVICE_SET_RUNTIME_VALUE_INT, SERVICE_PULSE) -from homeassistant.components.ihc.util import async_pulse from homeassistant.config import load_yaml_config_file from homeassistant.const import ( CONF_ID, CONF_NAME, CONF_PASSWORD, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, @@ -20,6 +13,14 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import HomeAssistantType +from .const import ( + ATTR_IHC_ID, ATTR_VALUE, CONF_AUTOSETUP, CONF_BINARY_SENSOR, CONF_DIMMABLE, + CONF_INFO, CONF_INVERTING, CONF_LIGHT, CONF_NODE, CONF_NOTE, CONF_OFF_ID, + CONF_ON_ID, CONF_POSITION, CONF_SENSOR, CONF_SWITCH, CONF_XPATH, + SERVICE_PULSE, SERVICE_SET_RUNTIME_VALUE_BOOL, + SERVICE_SET_RUNTIME_VALUE_FLOAT, SERVICE_SET_RUNTIME_VALUE_INT) +from .util import async_pulse + REQUIREMENTS = ['ihcsdk==2.3.0', 'defusedxml==0.5.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ihc/binary_sensor.py b/homeassistant/components/ihc/binary_sensor.py index 7e3371a834c1ad..69e3e1685af8c8 100644 --- a/homeassistant/components/ihc/binary_sensor.py +++ b/homeassistant/components/ihc/binary_sensor.py @@ -1,10 +1,11 @@ """Support for IHC binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.ihc import IHC_CONTROLLER, IHC_DATA, IHC_INFO -from homeassistant.components.ihc.const import CONF_INVERTING -from homeassistant.components.ihc.ihcdevice import IHCDevice from homeassistant.const import CONF_TYPE +from . import IHC_CONTROLLER, IHC_DATA, IHC_INFO +from .const import CONF_INVERTING +from .ihcdevice import IHCDevice + DEPENDENCIES = ['ihc'] diff --git a/homeassistant/components/ihc/light.py b/homeassistant/components/ihc/light.py index 646be7597d057c..ad6d0fb6511859 100644 --- a/homeassistant/components/ihc/light.py +++ b/homeassistant/components/ihc/light.py @@ -1,15 +1,14 @@ """Support for IHC lights.""" import logging -from homeassistant.components.ihc import IHC_CONTROLLER, IHC_DATA, IHC_INFO -from homeassistant.components.ihc.const import ( - CONF_DIMMABLE, CONF_OFF_ID, CONF_ON_ID) -from homeassistant.components.ihc.util import ( - async_pulse, async_set_bool, async_set_int) -from homeassistant.components.ihc.ihcdevice import IHCDevice from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) +from . import IHC_CONTROLLER, IHC_DATA, IHC_INFO +from .const import CONF_DIMMABLE, CONF_OFF_ID, CONF_ON_ID +from .ihcdevice import IHCDevice +from .util import async_pulse, async_set_bool, async_set_int + DEPENDENCIES = ['ihc'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ihc/sensor.py b/homeassistant/components/ihc/sensor.py index 930ac221629487..fd1f2cee53a846 100644 --- a/homeassistant/components/ihc/sensor.py +++ b/homeassistant/components/ihc/sensor.py @@ -1,9 +1,10 @@ """Support for IHC sensors.""" -from homeassistant.components.ihc import IHC_CONTROLLER, IHC_DATA, IHC_INFO -from homeassistant.components.ihc.ihcdevice import IHCDevice from homeassistant.const import CONF_UNIT_OF_MEASUREMENT from homeassistant.helpers.entity import Entity +from . import IHC_CONTROLLER, IHC_DATA, IHC_INFO +from .ihcdevice import IHCDevice + DEPENDENCIES = ['ihc'] diff --git a/homeassistant/components/ihc/switch.py b/homeassistant/components/ihc/switch.py index d25b343446dd05..e2189492b8f435 100644 --- a/homeassistant/components/ihc/switch.py +++ b/homeassistant/components/ihc/switch.py @@ -1,10 +1,11 @@ """Support for IHC switches.""" -from homeassistant.components.ihc import IHC_CONTROLLER, IHC_DATA, IHC_INFO -from homeassistant.components.ihc.const import CONF_OFF_ID, CONF_ON_ID -from homeassistant.components.ihc.util import async_pulse, async_set_bool -from homeassistant.components.ihc.ihcdevice import IHCDevice from homeassistant.components.switch import SwitchDevice +from . import IHC_CONTROLLER, IHC_DATA, IHC_INFO +from .const import CONF_OFF_ID, CONF_ON_ID +from .ihcdevice import IHCDevice +from .util import async_pulse, async_set_bool + DEPENDENCIES = ['ihc'] diff --git a/homeassistant/components/influxdb/sensor.py b/homeassistant/components/influxdb/sensor.py index 35229c2a80549d..9dbb4787df73d2 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -9,7 +9,6 @@ import voluptuous as vol -from homeassistant.components.influxdb import CONF_DB_NAME from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SSL, @@ -20,6 +19,8 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle +from . import CONF_DB_NAME + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['influxdb==5.2.0'] diff --git a/homeassistant/components/insteon/binary_sensor.py b/homeassistant/components/insteon/binary_sensor.py index 06eddb9a0040fb..6f1e56756394f0 100644 --- a/homeassistant/components/insteon/binary_sensor.py +++ b/homeassistant/components/insteon/binary_sensor.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.insteon import InsteonEntity + +from . import InsteonEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/insteon/cover.py b/homeassistant/components/insteon/cover.py index 7de2e872489e85..1bb316152a9db9 100644 --- a/homeassistant/components/insteon/cover.py +++ b/homeassistant/components/insteon/cover.py @@ -5,7 +5,8 @@ from homeassistant.components.cover import ( ATTR_POSITION, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, CoverDevice) -from homeassistant.components.insteon import InsteonEntity + +from . import InsteonEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/insteon/fan.py b/homeassistant/components/insteon/fan.py index 2b6097a4ba2f40..26a56d6df98b3b 100644 --- a/homeassistant/components/insteon/fan.py +++ b/homeassistant/components/insteon/fan.py @@ -4,9 +4,10 @@ from homeassistant.components.fan import ( SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_SET_SPEED, FanEntity) -from homeassistant.components.insteon import InsteonEntity from homeassistant.const import STATE_OFF +from . import InsteonEntity + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['insteon'] diff --git a/homeassistant/components/insteon/light.py b/homeassistant/components/insteon/light.py index e8ffc226716854..676c053325c65f 100644 --- a/homeassistant/components/insteon/light.py +++ b/homeassistant/components/insteon/light.py @@ -1,10 +1,11 @@ """Support for Insteon lights via PowerLinc Modem.""" import logging -from homeassistant.components.insteon import InsteonEntity from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) +from . import InsteonEntity + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['insteon'] diff --git a/homeassistant/components/insteon/sensor.py b/homeassistant/components/insteon/sensor.py index d895d972027005..edea87e1f738aa 100644 --- a/homeassistant/components/insteon/sensor.py +++ b/homeassistant/components/insteon/sensor.py @@ -1,9 +1,10 @@ """Support for INSTEON dimmers via PowerLinc Modem.""" import logging -from homeassistant.components.insteon import InsteonEntity from homeassistant.helpers.entity import Entity +from . import InsteonEntity + DEPENDENCIES = ['insteon'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/insteon/switch.py b/homeassistant/components/insteon/switch.py index 2a6b97a39d10be..4fdcdb20bb2847 100644 --- a/homeassistant/components/insteon/switch.py +++ b/homeassistant/components/insteon/switch.py @@ -1,9 +1,10 @@ """Support for INSTEON dimmers via PowerLinc Modem.""" import logging -from homeassistant.components.insteon import InsteonEntity from homeassistant.components.switch import SwitchDevice +from . import InsteonEntity + DEPENDENCIES = ['insteon'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/iota/sensor.py b/homeassistant/components/iota/sensor.py index 5cd5db6169be09..2955828aff5a77 100644 --- a/homeassistant/components/iota/sensor.py +++ b/homeassistant/components/iota/sensor.py @@ -1,10 +1,11 @@ """Support for IOTA wallet sensors.""" -import logging from datetime import timedelta +import logging -from homeassistant.components.iota import IotaDevice, CONF_WALLETS from homeassistant.const import CONF_NAME +from . import CONF_WALLETS, IotaDevice + _LOGGER = logging.getLogger(__name__) ATTR_TESTNET = 'testnet' diff --git a/homeassistant/components/iperf3/sensor.py b/homeassistant/components/iperf3/sensor.py index 59813ae0455ae3..db9aafcdf4bb19 100644 --- a/homeassistant/components/iperf3/sensor.py +++ b/homeassistant/components/iperf3/sensor.py @@ -1,11 +1,11 @@ """Support for Iperf3 sensors.""" -from homeassistant.components.iperf3 import ( - DATA_UPDATED, DOMAIN as IPERF3_DOMAIN, SENSOR_TYPES, ATTR_VERSION) from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity +from . import ATTR_VERSION, DATA_UPDATED, DOMAIN as IPERF3_DOMAIN, SENSOR_TYPES + DEPENDENCIES = ['iperf3'] ATTRIBUTION = 'Data retrieved using Iperf3' diff --git a/homeassistant/components/ipma/const.py b/homeassistant/components/ipma/const.py index bdd97c74e6acad..1e778eff5bdfe5 100644 --- a/homeassistant/components/ipma/const.py +++ b/homeassistant/components/ipma/const.py @@ -11,4 +11,4 @@ ENTITY_ID_SENSOR_FORMAT_HOME = ENTITY_ID_SENSOR_FORMAT.format( HOME_LOCATION_NAME) -_LOGGER = logging.getLogger('homeassistant.components.ipma') +_LOGGER = logging.getLogger('.') diff --git a/homeassistant/components/isy994/binary_sensor.py b/homeassistant/components/isy994/binary_sensor.py index 013b99fbb15a4f..ce95e71e8d419c 100644 --- a/homeassistant/components/isy994/binary_sensor.py +++ b/homeassistant/components/isy994/binary_sensor.py @@ -4,14 +4,14 @@ from typing import Callable from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_PROGRAMS, ISYDevice) from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import callback from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.typing import ConfigType from homeassistant.util import dt as dt_util +from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice + _LOGGER = logging.getLogger(__name__) ISY_DEVICE_TYPES = { diff --git a/homeassistant/components/isy994/cover.py b/homeassistant/components/isy994/cover.py index 22ea1629794238..b40d6428f2457d 100644 --- a/homeassistant/components/isy994/cover.py +++ b/homeassistant/components/isy994/cover.py @@ -3,12 +3,12 @@ from typing import Callable from homeassistant.components.cover import DOMAIN, CoverDevice -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_PROGRAMS, ISYDevice) from homeassistant.const import ( STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING, STATE_UNKNOWN) from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice + _LOGGER = logging.getLogger(__name__) VALUE_TO_STATE = { diff --git a/homeassistant/components/isy994/fan.py b/homeassistant/components/isy994/fan.py index 142eaedd66b866..5a21a28fd8d857 100644 --- a/homeassistant/components/isy994/fan.py +++ b/homeassistant/components/isy994/fan.py @@ -5,10 +5,10 @@ from homeassistant.components.fan import ( DOMAIN, SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_SET_SPEED, FanEntity) -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_PROGRAMS, ISYDevice) from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice + _LOGGER = logging.getLogger(__name__) VALUE_TO_STATE = { diff --git a/homeassistant/components/isy994/light.py b/homeassistant/components/isy994/light.py index cc39a6d1a3b1f6..0ac50a5f3849e7 100644 --- a/homeassistant/components/isy994/light.py +++ b/homeassistant/components/isy994/light.py @@ -2,10 +2,11 @@ import logging from typing import Callable -from homeassistant.components.isy994 import ISY994_NODES, ISYDevice from homeassistant.components.light import DOMAIN, SUPPORT_BRIGHTNESS, Light from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISYDevice + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/isy994/lock.py b/homeassistant/components/isy994/lock.py index a2e8b1a1e56de7..92cb317ed20eec 100644 --- a/homeassistant/components/isy994/lock.py +++ b/homeassistant/components/isy994/lock.py @@ -2,12 +2,12 @@ import logging from typing import Callable -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_PROGRAMS, ISYDevice) from homeassistant.components.lock import DOMAIN, LockDevice from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice + _LOGGER = logging.getLogger(__name__) VALUE_TO_STATE = { diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 2115c19f496efb..43c016ed4d1c99 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -2,13 +2,13 @@ import logging from typing import Callable -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_WEATHER, ISYDevice) from homeassistant.components.sensor import DOMAIN from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, UNIT_UV_INDEX, POWER_WATT) + POWER_WATT, TEMP_CELSIUS, TEMP_FAHRENHEIT, UNIT_UV_INDEX) from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISY994_WEATHER, ISYDevice + _LOGGER = logging.getLogger(__name__) UOM_FRIENDLY_NAME = { diff --git a/homeassistant/components/isy994/switch.py b/homeassistant/components/isy994/switch.py index 96f17c80befd4a..5f0acd1b1e259d 100644 --- a/homeassistant/components/isy994/switch.py +++ b/homeassistant/components/isy994/switch.py @@ -2,11 +2,11 @@ import logging from typing import Callable -from homeassistant.components.isy994 import ( - ISY994_NODES, ISY994_PROGRAMS, ISYDevice) from homeassistant.components.switch import DOMAIN, SwitchDevice from homeassistant.helpers.typing import ConfigType +from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/juicenet/sensor.py b/homeassistant/components/juicenet/sensor.py index 00b183fca46ac4..6b55e539547d24 100644 --- a/homeassistant/components/juicenet/sensor.py +++ b/homeassistant/components/juicenet/sensor.py @@ -1,9 +1,10 @@ """Support for monitoring juicenet/juicepoint/juicebox based EVSE sensors.""" import logging -from homeassistant.const import TEMP_CELSIUS, POWER_WATT, ENERGY_WATT_HOUR +from homeassistant.const import ENERGY_WATT_HOUR, POWER_WATT, TEMP_CELSIUS from homeassistant.helpers.entity import Entity -from homeassistant.components.juicenet import JuicenetDevice, DOMAIN + +from . import DOMAIN, JuicenetDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index c84e5820f04618..8ee21e24c5e978 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -3,12 +3,12 @@ from homeassistant.components.binary_sensor import ( PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.knx import ( - ATTR_DISCOVER_DEVICES, DATA_KNX, KNXAutomation) from homeassistant.const import CONF_ADDRESS, CONF_DEVICE_CLASS, CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import ATTR_DISCOVER_DEVICES, DATA_KNX, KNXAutomation + CONF_SIGNIFICANT_BIT = 'significant_bit' CONF_DEFAULT_SIGNIFICANT_BIT = 1 CONF_AUTOMATION = 'automation' diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index 921b2936d97458..e11e5449326bf5 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -5,11 +5,12 @@ from homeassistant.components.climate.const import ( STATE_DRY, STATE_ECO, STATE_FAN_ONLY, STATE_HEAT, STATE_IDLE, STATE_MANUAL, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + CONF_SETPOINT_SHIFT_ADDRESS = 'setpoint_shift_address' CONF_SETPOINT_SHIFT_STATE_ADDRESS = 'setpoint_shift_state_address' CONF_SETPOINT_SHIFT_STEP = 'setpoint_shift_step' diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index 9423983f9f758f..b2b287d1e87d23 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -5,12 +5,13 @@ ATTR_POSITION, ATTR_TILT_POSITION, PLATFORM_SCHEMA, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, SUPPORT_SET_TILT_POSITION, SUPPORT_STOP, CoverDevice) -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.const import CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_utc_time_change +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + CONF_MOVE_LONG_ADDRESS = 'move_long_address' CONF_MOVE_SHORT_ADDRESS = 'move_short_address' CONF_POSITION_ADDRESS = 'position_address' diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index baba7edd21aa8e..cf59f1fc135b3b 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -3,7 +3,6 @@ import voluptuous as vol -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) @@ -12,6 +11,8 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + CONF_STATE_ADDRESS = 'state_address' CONF_BRIGHTNESS_ADDRESS = 'brightness_address' CONF_BRIGHTNESS_STATE_ADDRESS = 'brightness_state_address' diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index 1e1d7f185f0cf5..742252d187426f 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -1,13 +1,14 @@ """Support for KNX/IP notification services.""" import voluptuous as vol -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.components.notify import ( PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ADDRESS, CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + DEFAULT_NAME = 'KNX Notify' DEPENDENCIES = ['knx'] diff --git a/homeassistant/components/knx/scene.py b/homeassistant/components/knx/scene.py index b1bb2bf3109014..4bf186c28ff7d4 100644 --- a/homeassistant/components/knx/scene.py +++ b/homeassistant/components/knx/scene.py @@ -1,12 +1,13 @@ """Support for KNX scenes.""" import voluptuous as vol -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.components.scene import CONF_PLATFORM, Scene from homeassistant.const import CONF_ADDRESS, CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + CONF_SCENE_NUMBER = 'scene_number' DEFAULT_NAME = 'KNX SCENE' diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index abbb61e150d694..7ddafe53be44b0 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -1,13 +1,14 @@ """Support for KNX/IP sensors.""" import voluptuous as vol -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_TYPE from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + DEFAULT_NAME = 'KNX Sensor' DEPENDENCIES = ['knx'] diff --git a/homeassistant/components/knx/switch.py b/homeassistant/components/knx/switch.py index cef14fb74dc126..e3beff39677954 100644 --- a/homeassistant/components/knx/switch.py +++ b/homeassistant/components/knx/switch.py @@ -1,12 +1,13 @@ """Support for KNX/IP switches.""" import voluptuous as vol -from homeassistant.components.knx import ATTR_DISCOVER_DEVICES, DATA_KNX from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import CONF_ADDRESS, CONF_NAME from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import ATTR_DISCOVER_DEVICES, DATA_KNX + CONF_STATE_ADDRESS = 'state_address' DEFAULT_NAME = 'KNX Switch' diff --git a/homeassistant/components/konnected/binary_sensor.py b/homeassistant/components/konnected/binary_sensor.py index a47f81b9556315..1fbfbea1861c25 100644 --- a/homeassistant/components/konnected/binary_sensor.py +++ b/homeassistant/components/konnected/binary_sensor.py @@ -2,14 +2,14 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.konnected import ( - DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, SIGNAL_SENSOR_UPDATE) from homeassistant.const import ( - CONF_DEVICES, CONF_TYPE, CONF_NAME, CONF_BINARY_SENSORS, ATTR_ENTITY_ID, - ATTR_STATE) + ATTR_ENTITY_ID, ATTR_STATE, CONF_BINARY_SENSORS, CONF_DEVICES, CONF_NAME, + CONF_TYPE) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, SIGNAL_SENSOR_UPDATE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['konnected'] diff --git a/homeassistant/components/konnected/sensor.py b/homeassistant/components/konnected/sensor.py index eb3f5511346ba6..a48d1a58619600 100644 --- a/homeassistant/components/konnected/sensor.py +++ b/homeassistant/components/konnected/sensor.py @@ -1,15 +1,16 @@ """Support for DHT and DS18B20 sensors attached to a Konnected device.""" import logging -from homeassistant.components.konnected.const import ( - DOMAIN as KONNECTED_DOMAIN, SIGNAL_DS18B20_NEW, SIGNAL_SENSOR_UPDATE) from homeassistant.const import ( - CONF_DEVICES, CONF_PIN, CONF_TYPE, CONF_NAME, CONF_SENSORS, + CONF_DEVICES, CONF_NAME, CONF_PIN, CONF_SENSORS, CONF_TYPE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +from .const import ( + DOMAIN as KONNECTED_DOMAIN, SIGNAL_DS18B20_NEW, SIGNAL_SENSOR_UPDATE) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['konnected'] diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index 1a4b495297e1ca..dfb135e19f618b 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -1,12 +1,13 @@ """Support for wired switches attached to a Konnected device.""" import logging -from homeassistant.components.konnected import ( - DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, CONF_ACTIVATION, CONF_MOMENTARY, - CONF_PAUSE, CONF_REPEAT, STATE_LOW, STATE_HIGH) -from homeassistant.helpers.entity import ToggleEntity from homeassistant.const import ( ATTR_STATE, CONF_DEVICES, CONF_NAME, CONF_PIN, CONF_SWITCHES) +from homeassistant.helpers.entity import ToggleEntity + +from . import ( + CONF_ACTIVATION, CONF_MOMENTARY, CONF_PAUSE, CONF_REPEAT, + DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, STATE_HIGH, STATE_LOW) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lametric/notify.py b/homeassistant/components/lametric/notify.py index 9903676d9f965e..358bb056b00e33 100644 --- a/homeassistant/components/lametric/notify.py +++ b/homeassistant/components/lametric/notify.py @@ -5,11 +5,11 @@ import voluptuous as vol from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) + ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ICON import homeassistant.helpers.config_validation as cv -from homeassistant.components.lametric import DOMAIN as LAMETRIC_DOMAIN +from . import DOMAIN as LAMETRIC_DOMAIN REQUIREMENTS = ['lmnotify==0.0.4'] diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index 6b995643443292..e380c2bb4a1cfd 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -3,12 +3,6 @@ import voluptuous as vol -from homeassistant.components.lcn.const import ( - CONF_CONNECTIONS, CONF_DIM_MODE, CONF_DIMMABLE, CONF_MOTOR, CONF_OUTPUT, - CONF_SK_NUM_TRIES, CONF_SOURCE, CONF_TRANSITION, DATA_LCN, DEFAULT_NAME, - DIM_MODES, DOMAIN, LED_PORTS, LOGICOP_PORTS, MOTOR_PORTS, OUTPUT_PORTS, - PATTERN_ADDRESS, RELAY_PORTS, S0_INPUTS, SETPOINTS, THRESHOLDS, VAR_UNITS, - VARIABLES) from homeassistant.const import ( CONF_ADDRESS, CONF_COVERS, CONF_HOST, CONF_LIGHTS, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SENSORS, CONF_SWITCHES, @@ -17,6 +11,13 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.entity import Entity +from .const import ( + CONF_CONNECTIONS, CONF_DIM_MODE, CONF_DIMMABLE, CONF_MOTOR, CONF_OUTPUT, + CONF_SK_NUM_TRIES, CONF_SOURCE, CONF_TRANSITION, DATA_LCN, DEFAULT_NAME, + DIM_MODES, DOMAIN, LED_PORTS, LOGICOP_PORTS, MOTOR_PORTS, OUTPUT_PORTS, + PATTERN_ADDRESS, RELAY_PORTS, S0_INPUTS, SETPOINTS, THRESHOLDS, VAR_UNITS, + VARIABLES) + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pypck==0.5.9'] diff --git a/homeassistant/components/lcn/cover.py b/homeassistant/components/lcn/cover.py index 4b4542fd6236c3..a32ff7c23f4605 100755 --- a/homeassistant/components/lcn/cover.py +++ b/homeassistant/components/lcn/cover.py @@ -1,10 +1,10 @@ """Support for LCN covers.""" from homeassistant.components.cover import CoverDevice -from homeassistant.components.lcn import LcnDevice, get_connection -from homeassistant.components.lcn.const import ( - CONF_CONNECTIONS, CONF_MOTOR, DATA_LCN) from homeassistant.const import CONF_ADDRESS +from . import LcnDevice, get_connection +from .const import CONF_CONNECTIONS, CONF_MOTOR, DATA_LCN + DEPENDENCIES = ['lcn'] diff --git a/homeassistant/components/lcn/light.py b/homeassistant/components/lcn/light.py index 5f1008cbd57a5b..00b78259354e51 100644 --- a/homeassistant/components/lcn/light.py +++ b/homeassistant/components/lcn/light.py @@ -1,13 +1,14 @@ """Support for LCN lights.""" -from homeassistant.components.lcn import LcnDevice, get_connection -from homeassistant.components.lcn.const import ( - CONF_CONNECTIONS, CONF_DIMMABLE, CONF_OUTPUT, CONF_TRANSITION, DATA_LCN, - OUTPUT_PORTS) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_TRANSITION, SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION, Light) from homeassistant.const import CONF_ADDRESS +from . import LcnDevice, get_connection +from .const import ( + CONF_CONNECTIONS, CONF_DIMMABLE, CONF_OUTPUT, CONF_TRANSITION, DATA_LCN, + OUTPUT_PORTS) + DEPENDENCIES = ['lcn'] diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py index e56c42de058bda..5e50d092ada05d 100755 --- a/homeassistant/components/lcn/sensor.py +++ b/homeassistant/components/lcn/sensor.py @@ -1,9 +1,10 @@ """Support for LCN sensors.""" -from homeassistant.components.lcn import LcnDevice, get_connection -from homeassistant.components.lcn.const import ( +from homeassistant.const import CONF_ADDRESS, CONF_UNIT_OF_MEASUREMENT + +from . import LcnDevice, get_connection +from .const import ( CONF_CONNECTIONS, CONF_SOURCE, DATA_LCN, LED_PORTS, S0_INPUTS, SETPOINTS, THRESHOLDS, VARIABLES) -from homeassistant.const import CONF_ADDRESS, CONF_UNIT_OF_MEASUREMENT DEPENDENCIES = ['lcn'] diff --git a/homeassistant/components/lcn/switch.py b/homeassistant/components/lcn/switch.py index 09f35d267180cb..7c375f4a598c79 100755 --- a/homeassistant/components/lcn/switch.py +++ b/homeassistant/components/lcn/switch.py @@ -1,10 +1,10 @@ """Support for LCN switches.""" -from homeassistant.components.lcn import LcnDevice, get_connection -from homeassistant.components.lcn.const import ( - CONF_CONNECTIONS, CONF_OUTPUT, DATA_LCN, OUTPUT_PORTS) from homeassistant.components.switch import SwitchDevice from homeassistant.const import CONF_ADDRESS +from . import LcnDevice, get_connection +from .const import CONF_CONNECTIONS, CONF_OUTPUT, DATA_LCN, OUTPUT_PORTS + DEPENDENCIES = ['lcn'] diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 19a9f7583ecef9..014ca9ae6c80b5 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -16,9 +16,6 @@ SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_TRANSITION, VALID_BRIGHTNESS, VALID_BRIGHTNESS_PCT, Light, preprocess_turn_on_alternatives) -from homeassistant.components.lifx import ( - DOMAIN as LIFX_DOMAIN, DATA_LIFX_MANAGER, CONF_SERVER, CONF_PORT, - CONF_BROADCAST) from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_STOP from homeassistant.core import callback import homeassistant.helpers.config_validation as cv @@ -27,6 +24,10 @@ from homeassistant.helpers.service import async_extract_entity_ids import homeassistant.util.color as color_util +from . import ( + CONF_BROADCAST, CONF_PORT, CONF_SERVER, DATA_LIFX_MANAGER, + DOMAIN as LIFX_DOMAIN) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['lifx'] diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/light/demo.py index 980d849174463b..d9affb03db3756 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/light/demo.py @@ -6,10 +6,10 @@ """ import random -from homeassistant.components.light import ( +from . import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, - ATTR_WHITE_VALUE, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, - SUPPORT_COLOR, SUPPORT_WHITE_VALUE, Light) + ATTR_WHITE_VALUE, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, + SUPPORT_EFFECT, SUPPORT_WHITE_VALUE, Light) LIGHT_COLORS = [ (56, 86), diff --git a/homeassistant/components/light/group.py b/homeassistant/components/light/group.py index bf54d3ecf29586..7e9d11d3a02329 100644 --- a/homeassistant/components/light/group.py +++ b/homeassistant/components/light/group.py @@ -4,27 +4,28 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.group/ """ -import logging -import itertools -from typing import List, Tuple, Optional, Iterator, Any, Callable from collections import Counter +import itertools +import logging +from typing import Any, Callable, Iterator, List, Optional, Tuple import voluptuous as vol -from homeassistant.core import State, callback from homeassistant.components import light -from homeassistant.const import (STATE_ON, ATTR_ENTITY_ID, CONF_NAME, - CONF_ENTITIES, STATE_UNAVAILABLE, - ATTR_SUPPORTED_FEATURES) -from homeassistant.helpers.event import async_track_state_change -from homeassistant.helpers.typing import HomeAssistantType, ConfigType -from homeassistant.components.light import ( - SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_TRANSITION, - SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_WHITE_VALUE, PLATFORM_SCHEMA, - ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_WHITE_VALUE, ATTR_COLOR_TEMP, - ATTR_MIN_MIREDS, ATTR_MAX_MIREDS, ATTR_EFFECT_LIST, ATTR_EFFECT, - ATTR_FLASH, ATTR_TRANSITION) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, CONF_NAME, + STATE_ON, STATE_UNAVAILABLE) +from homeassistant.core import State, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.typing import ConfigType, HomeAssistantType + +from . import ( + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_EFFECT_LIST, + ATTR_FLASH, ATTR_HS_COLOR, ATTR_MAX_MIREDS, ATTR_MIN_MIREDS, + ATTR_TRANSITION, ATTR_WHITE_VALUE, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_FLASH, + SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/light/switch.py b/homeassistant/components/light/switch.py index de6247a2772f8d..4b4f33133490d3 100644 --- a/homeassistant/components/light/switch.py +++ b/homeassistant/components/light/switch.py @@ -5,22 +5,18 @@ https://home-assistant.io/components/light.switch/ """ import logging + import voluptuous as vol -from homeassistant.core import State, callback -from homeassistant.components.light import ( - Light, PLATFORM_SCHEMA) from homeassistant.components import switch from homeassistant.const import ( - STATE_ON, - ATTR_ENTITY_ID, - CONF_NAME, - CONF_ENTITY_ID, - STATE_UNAVAILABLE -) -from homeassistant.helpers.typing import HomeAssistantType, ConfigType -from homeassistant.helpers.event import async_track_state_change + ATTR_ENTITY_ID, CONF_ENTITY_ID, CONF_NAME, STATE_ON, STATE_UNAVAILABLE) +from homeassistant.core import State, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.typing import ConfigType, HomeAssistantType + +from . import PLATFORM_SCHEMA, Light _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lightwave/light.py b/homeassistant/components/lightwave/light.py index 1dfbac37c889ab..f22533d2548dca 100644 --- a/homeassistant/components/lightwave/light.py +++ b/homeassistant/components/lightwave/light.py @@ -1,9 +1,10 @@ """Support for LightwaveRF lights.""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.lightwave import LIGHTWAVE_LINK from homeassistant.const import CONF_NAME +from . import LIGHTWAVE_LINK + DEPENDENCIES = ['lightwave'] MAX_BRIGHTNESS = 255 diff --git a/homeassistant/components/lightwave/switch.py b/homeassistant/components/lightwave/switch.py index d6c00b7fddb66d..dfa93b4b151844 100644 --- a/homeassistant/components/lightwave/switch.py +++ b/homeassistant/components/lightwave/switch.py @@ -1,8 +1,9 @@ """Support for LightwaveRF switches.""" -from homeassistant.components.lightwave import LIGHTWAVE_LINK from homeassistant.components.switch import SwitchDevice from homeassistant.const import CONF_NAME +from . import LIGHTWAVE_LINK + DEPENDENCIES = ['lightwave'] diff --git a/homeassistant/components/linode/binary_sensor.py b/homeassistant/components/linode/binary_sensor.py index a05681497de165..19455917dbb28b 100644 --- a/homeassistant/components/linode/binary_sensor.py +++ b/homeassistant/components/linode/binary_sensor.py @@ -5,11 +5,12 @@ from homeassistant.components.binary_sensor import ( PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.linode import ( +import homeassistant.helpers.config_validation as cv + +from . import ( ATTR_CREATED, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_NODE_ID, ATTR_NODE_NAME, ATTR_REGION, ATTR_VCPUS, CONF_NODES, DATA_LINODE) -import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/linode/switch.py b/homeassistant/components/linode/switch.py index 0cab2f4d0f25fa..e5f97ef756e403 100644 --- a/homeassistant/components/linode/switch.py +++ b/homeassistant/components/linode/switch.py @@ -3,12 +3,13 @@ import voluptuous as vol -from homeassistant.components.linode import ( +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +import homeassistant.helpers.config_validation as cv + +from . import ( ATTR_CREATED, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_NODE_ID, ATTR_NODE_NAME, ATTR_REGION, ATTR_VCPUS, CONF_NODES, DATA_LINODE) -from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice -import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/locative/device_tracker.py b/homeassistant/components/locative/device_tracker.py index 78090914b2c98c..9dbf7e74fe33e8 100644 --- a/homeassistant/components/locative/device_tracker.py +++ b/homeassistant/components/locative/device_tracker.py @@ -6,13 +6,13 @@ """ import logging -from homeassistant.components.device_tracker import \ - DOMAIN as DEVICE_TRACKER_DOMAIN -from homeassistant.components.locative import DOMAIN as LOCATIVE_DOMAIN -from homeassistant.components.locative import TRACKER_UPDATE +from homeassistant.components.device_tracker import ( + DOMAIN as DEVICE_TRACKER_DOMAIN) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import slugify +from . import DOMAIN as LOCATIVE_DOMAIN, TRACKER_UPDATE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['locative'] diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index a0cc45991c822f..94a67fb87f6d0c 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -4,8 +4,9 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.lock import LockDevice, SUPPORT_OPEN -from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED) +from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED + +from . import SUPPORT_OPEN, LockDevice def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/logi_circle/camera.py b/homeassistant/components/logi_circle/camera.py index 4f349dd986e449..814475d04de916 100644 --- a/homeassistant/components/logi_circle/camera.py +++ b/homeassistant/components/logi_circle/camera.py @@ -1,19 +1,19 @@ """Support to the Logi Circle cameras.""" -import logging import asyncio from datetime import timedelta +import logging import voluptuous as vol -from homeassistant.helpers import config_validation as cv -from homeassistant.components.logi_circle import ( - DOMAIN as LOGI_CIRCLE_DOMAIN, ATTRIBUTION) from homeassistant.components.camera import ( - Camera, PLATFORM_SCHEMA, CAMERA_SERVICE_SCHEMA, SUPPORT_ON_OFF, - ATTR_ENTITY_ID, ATTR_FILENAME, DOMAIN) + ATTR_ENTITY_ID, ATTR_FILENAME, CAMERA_SERVICE_SCHEMA, DOMAIN, + PLATFORM_SCHEMA, SUPPORT_ON_OFF, Camera) from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, - CONF_SCAN_INTERVAL, STATE_ON, STATE_OFF) + CONF_SCAN_INTERVAL, STATE_OFF, STATE_ON) +from homeassistant.helpers import config_validation as cv + +from . import ATTRIBUTION, DOMAIN as LOGI_CIRCLE_DOMAIN DEPENDENCIES = ['logi_circle'] diff --git a/homeassistant/components/logi_circle/sensor.py b/homeassistant/components/logi_circle/sensor.py index 4830219091cdc1..06d1701a9ebd12 100644 --- a/homeassistant/components/logi_circle/sensor.py +++ b/homeassistant/components/logi_circle/sensor.py @@ -3,18 +3,18 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.logi_circle import ( - ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DOMAIN as LOGI_CIRCLE_DOMAIN) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, - CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS, - STATE_ON, STATE_OFF) + ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, CONF_ENTITY_NAMESPACE, + CONF_MONITORED_CONDITIONS, STATE_OFF, STATE_ON) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.util.dt import as_local +from . import ( + ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DOMAIN as LOGI_CIRCLE_DOMAIN) + DEPENDENCIES = ['logi_circle'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/luftdaten/sensor.py b/homeassistant/components/luftdaten/sensor.py index 398ec30a3f5a11..107673bac45214 100644 --- a/homeassistant/components/luftdaten/sensor.py +++ b/homeassistant/components/luftdaten/sensor.py @@ -1,16 +1,17 @@ """Support for Luftdaten sensors.""" import logging -from homeassistant.components.luftdaten import ( - DATA_LUFTDATEN, DATA_LUFTDATEN_CLIENT, DEFAULT_ATTRIBUTION, DOMAIN, - SENSORS, TOPIC_UPDATE) -from homeassistant.components.luftdaten.const import ATTR_SENSOR_ID from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_SHOW_ON_MAP) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +from . import ( + DATA_LUFTDATEN, DATA_LUFTDATEN_CLIENT, DEFAULT_ATTRIBUTION, DOMAIN, + SENSORS, TOPIC_UPDATE) +from .const import ATTR_SENSOR_ID + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['luftdaten'] diff --git a/homeassistant/components/lupusec/alarm_control_panel.py b/homeassistant/components/lupusec/alarm_control_panel.py index de62e5bfac2cb8..0a88f3bd552fc4 100644 --- a/homeassistant/components/lupusec/alarm_control_panel.py +++ b/homeassistant/components/lupusec/alarm_control_panel.py @@ -2,12 +2,11 @@ from datetime import timedelta from homeassistant.components.alarm_control_panel import AlarmControlPanel -from homeassistant.components.lupusec import DOMAIN as LUPUSEC_DOMAIN -from homeassistant.components.lupusec import LupusecDevice -from homeassistant.const import (STATE_ALARM_ARMED_AWAY, - STATE_ALARM_ARMED_HOME, - STATE_ALARM_DISARMED, - STATE_ALARM_TRIGGERED) +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, + STATE_ALARM_TRIGGERED) + +from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice DEPENDENCIES = ['lupusec'] diff --git a/homeassistant/components/lupusec/binary_sensor.py b/homeassistant/components/lupusec/binary_sensor.py index 8a5e103db0dd3f..2c3f5e0e0b86e0 100644 --- a/homeassistant/components/lupusec/binary_sensor.py +++ b/homeassistant/components/lupusec/binary_sensor.py @@ -1,11 +1,11 @@ """Support for Lupusec Security System binary sensors.""" -import logging from datetime import timedelta +import logging + +from homeassistant.components.binary_sensor import ( + DEVICE_CLASSES, BinarySensorDevice) -from homeassistant.components.lupusec import (LupusecDevice, - DOMAIN as LUPUSEC_DOMAIN) -from homeassistant.components.binary_sensor import (BinarySensorDevice, - DEVICE_CLASSES) +from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice DEPENDENCIES = ['lupusec'] diff --git a/homeassistant/components/lupusec/switch.py b/homeassistant/components/lupusec/switch.py index 8a30d65fec3086..0d86ea0a3650bf 100644 --- a/homeassistant/components/lupusec/switch.py +++ b/homeassistant/components/lupusec/switch.py @@ -1,11 +1,11 @@ """Support for Lupusec Security System switches.""" -import logging from datetime import timedelta +import logging -from homeassistant.components.lupusec import (LupusecDevice, - DOMAIN as LUPUSEC_DOMAIN) from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice + DEPENDENCIES = ['lupusec'] SCAN_INTERVAL = timedelta(seconds=2) diff --git a/homeassistant/components/lutron/cover.py b/homeassistant/components/lutron/cover.py index cc7a57a552224a..da7f69095fc436 100644 --- a/homeassistant/components/lutron/cover.py +++ b/homeassistant/components/lutron/cover.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.cover import ( - CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION, - ATTR_POSITION) -from homeassistant.components.lutron import ( - LutronDevice, LUTRON_DEVICES, LUTRON_CONTROLLER) + ATTR_POSITION, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, + CoverDevice) + +from . import LUTRON_CONTROLLER, LUTRON_DEVICES, LutronDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron/light.py b/homeassistant/components/lutron/light.py index c0b3b991147050..5f3fd4787fd3ef 100644 --- a/homeassistant/components/lutron/light.py +++ b/homeassistant/components/lutron/light.py @@ -3,8 +3,8 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.lutron import ( - LutronDevice, LUTRON_DEVICES, LUTRON_CONTROLLER) + +from . import LUTRON_CONTROLLER, LUTRON_DEVICES, LutronDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron/scene.py b/homeassistant/components/lutron/scene.py index f9002f2a8398c4..a2d18c6d242407 100644 --- a/homeassistant/components/lutron/scene.py +++ b/homeassistant/components/lutron/scene.py @@ -1,10 +1,10 @@ """Support for Lutron scenes.""" import logging -from homeassistant.components.lutron import ( - LutronDevice, LUTRON_DEVICES, LUTRON_CONTROLLER) from homeassistant.components.scene import Scene +from . import LUTRON_CONTROLLER, LUTRON_DEVICES, LutronDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['lutron'] diff --git a/homeassistant/components/lutron/switch.py b/homeassistant/components/lutron/switch.py index bfdb06be33c99a..b42c0d930bc834 100644 --- a/homeassistant/components/lutron/switch.py +++ b/homeassistant/components/lutron/switch.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.switch import SwitchDevice -from homeassistant.components.lutron import ( - LutronDevice, LUTRON_DEVICES, LUTRON_CONTROLLER) + +from . import LUTRON_CONTROLLER, LUTRON_DEVICES, LutronDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron_caseta/cover.py b/homeassistant/components/lutron_caseta/cover.py index 5e09dcc3c85822..d970f5282ff003 100644 --- a/homeassistant/components/lutron_caseta/cover.py +++ b/homeassistant/components/lutron_caseta/cover.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.cover import ( - CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION, - ATTR_POSITION, DOMAIN) -from homeassistant.components.lutron_caseta import ( - LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice) + ATTR_POSITION, DOMAIN, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, + CoverDevice) + +from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron_caseta/light.py b/homeassistant/components/lutron_caseta/light.py index 3bab781f3b609d..d883da73c9161a 100644 --- a/homeassistant/components/lutron_caseta/light.py +++ b/homeassistant/components/lutron_caseta/light.py @@ -2,11 +2,11 @@ import logging from homeassistant.components.light import ( - ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, DOMAIN) + ATTR_BRIGHTNESS, DOMAIN, SUPPORT_BRIGHTNESS, Light) from homeassistant.components.lutron.light import ( to_hass_level, to_lutron_level) -from homeassistant.components.lutron_caseta import ( - LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice) + +from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron_caseta/scene.py b/homeassistant/components/lutron_caseta/scene.py index c6ca7bad3ac966..2e7059a56fc0a2 100644 --- a/homeassistant/components/lutron_caseta/scene.py +++ b/homeassistant/components/lutron_caseta/scene.py @@ -1,9 +1,10 @@ """Support for Lutron Caseta scenes.""" import logging -from homeassistant.components.lutron_caseta import LUTRON_CASETA_SMARTBRIDGE from homeassistant.components.scene import Scene +from . import LUTRON_CASETA_SMARTBRIDGE + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['lutron_caseta'] diff --git a/homeassistant/components/lutron_caseta/switch.py b/homeassistant/components/lutron_caseta/switch.py index 0ef0595187b208..54c670913576ce 100644 --- a/homeassistant/components/lutron_caseta/switch.py +++ b/homeassistant/components/lutron_caseta/switch.py @@ -1,9 +1,9 @@ """Support for Lutron Caseta switches.""" import logging -from homeassistant.components.lutron_caseta import ( - LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice) -from homeassistant.components.switch import SwitchDevice, DOMAIN +from homeassistant.components.switch import DOMAIN, SwitchDevice + +from . import LUTRON_CASETA_SMARTBRIDGE, LutronCasetaDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mailgun/notify.py b/homeassistant/components/mailgun/notify.py index 05137254fcccbf..b9f5bf0b10000f 100644 --- a/homeassistant/components/mailgun/notify.py +++ b/homeassistant/components/mailgun/notify.py @@ -3,14 +3,14 @@ import voluptuous as vol -from homeassistant.components.mailgun import ( - CONF_SANDBOX, DOMAIN as MAILGUN_DOMAIN) from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService, ATTR_TITLE, ATTR_TITLE_DEFAULT, - ATTR_DATA) + ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) from homeassistant.const import ( CONF_API_KEY, CONF_DOMAIN, CONF_RECIPIENT, CONF_SENDER) +from . import CONF_SANDBOX, DOMAIN as MAILGUN_DOMAIN + REQUIREMENTS = ['pymailgunner==1.4'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/maxcube/binary_sensor.py b/homeassistant/components/maxcube/binary_sensor.py index 8d5ab84f6d3759..6221b95d879b73 100644 --- a/homeassistant/components/maxcube/binary_sensor.py +++ b/homeassistant/components/maxcube/binary_sensor.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.maxcube import DATA_KEY + +from . import DATA_KEY _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/maxcube/climate.py b/homeassistant/components/maxcube/climate.py index 170a3ba349cc0a..c30ebc7d697713 100644 --- a/homeassistant/components/maxcube/climate.py +++ b/homeassistant/components/maxcube/climate.py @@ -1,13 +1,13 @@ """Support for MAX! Thermostats via MAX! Cube.""" -import socket import logging +import socket from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_OPERATION_MODE) -from homeassistant.components.maxcube import DATA_KEY -from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE + STATE_AUTO, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS + +from . import DATA_KEY _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/media_player/demo.py b/homeassistant/components/media_player/demo.py index de455879d3d5ab..070701f6322be7 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/media_player/demo.py @@ -4,16 +4,16 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ +from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING import homeassistant.util.dt as dt_util -from homeassistant.components.media_player import ( - MediaPlayerDevice) -from homeassistant.components.media_player.const import ( + +from . import MediaPlayerDevice +from .const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOUND_MODE, SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) -from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/melissa/climate.py b/homeassistant/components/melissa/climate.py index b9eb28a61d75fa..0df294a148d9c6 100644 --- a/homeassistant/components/melissa/climate.py +++ b/homeassistant/components/melissa/climate.py @@ -8,16 +8,15 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_ON_OFF, STATE_AUTO, STATE_HEAT, STATE_COOL, STATE_DRY, - STATE_FAN_ONLY, SUPPORT_FAN_MODE -) -from homeassistant.components.fan import SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH -from homeassistant.components.melissa import DATA_MELISSA + STATE_AUTO, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, STATE_HEAT, + SUPPORT_FAN_MODE, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) +from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM from homeassistant.const import ( - TEMP_CELSIUS, STATE_ON, STATE_OFF, STATE_IDLE, ATTR_TEMPERATURE, - PRECISION_WHOLE -) + ATTR_TEMPERATURE, PRECISION_WHOLE, STATE_IDLE, STATE_OFF, STATE_ON, + TEMP_CELSIUS) + +from . import DATA_MELISSA DEPENDENCIES = ['melissa'] diff --git a/homeassistant/components/meteo_france/sensor.py b/homeassistant/components/meteo_france/sensor.py index f0ef926793e0a9..122b91cae44d63 100644 --- a/homeassistant/components/meteo_france/sensor.py +++ b/homeassistant/components/meteo_france/sensor.py @@ -1,11 +1,11 @@ """Support for Meteo-France raining forecast sensor.""" import logging -from homeassistant.components.meteo_france import ( - ATTRIBUTION, CONF_CITY, DATA_METEO_FRANCE, SENSOR_TYPES) from homeassistant.const import ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS from homeassistant.helpers.entity import Entity +from . import ATTRIBUTION, CONF_CITY, DATA_METEO_FRANCE, SENSOR_TYPES + _LOGGER = logging.getLogger(__name__) STATE_ATTR_FORECAST = '1h rain forecast' diff --git a/homeassistant/components/meteo_france/weather.py b/homeassistant/components/meteo_france/weather.py index 849c9d9da10de5..b2b94c7622e464 100644 --- a/homeassistant/components/meteo_france/weather.py +++ b/homeassistant/components/meteo_france/weather.py @@ -2,13 +2,13 @@ from datetime import datetime, timedelta import logging -from homeassistant.components.meteo_france import ( - ATTRIBUTION, CONDITION_CLASSES, CONF_CITY, DATA_METEO_FRANCE) from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, WeatherEntity) from homeassistant.const import TEMP_CELSIUS +from . import ATTRIBUTION, CONDITION_CLASSES, CONF_CITY, DATA_METEO_FRANCE + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/metoffice/weather.py b/homeassistant/components/metoffice/weather.py index b4984bc3cad02e..a67dcdcdbd67c7 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -3,13 +3,13 @@ import voluptuous as vol -from homeassistant.components.metoffice.sensor import ( - CONDITION_CLASSES, ATTRIBUTION, MetOfficeCurrentData) from homeassistant.components.weather import PLATFORM_SCHEMA, WeatherEntity from homeassistant.const import ( CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers import config_validation as cv +from .sensor import ATTRIBUTION, CONDITION_CLASSES, MetOfficeCurrentData + REQUIREMENTS = ['datapoint==0.4.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index 4e0ab74445d2f1..0c10548452a01d 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -4,12 +4,12 @@ import voluptuous as vol from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.modbus import ( - CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_NAME, CONF_SLAVE from homeassistant.helpers import config_validation as cv +from . import CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN + _LOGGER = logging.getLogger(__name__) CONF_COIL = 'coil' diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index 44daedac9c11b1..4d2b86903e770d 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -6,11 +6,11 @@ from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice from homeassistant.components.climate.const import SUPPORT_TARGET_TEMPERATURE -from homeassistant.components.modbus import ( - CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, CONF_SLAVE import homeassistant.helpers.config_validation as cv +from . import CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN + _LOGGER = logging.getLogger(__name__) CONF_TARGET_TEMP = 'target_temp_register' diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index 3f8c68b25ff142..10e11a9a656263 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -4,8 +4,6 @@ import voluptuous as vol -from homeassistant.components.modbus import ( - CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_NAME, CONF_OFFSET, CONF_SLAVE, CONF_STRUCTURE, @@ -13,6 +11,8 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.restore_state import RestoreEntity +from . import CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN + _LOGGER = logging.getLogger(__name__) CONF_COUNT = 'count' diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index b7039a01da3392..69c5e3e483888a 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -3,8 +3,6 @@ import voluptuous as vol -from homeassistant.components.modbus import ( - CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_COMMAND_OFF, CONF_COMMAND_ON, CONF_NAME, CONF_SLAVE, STATE_ON) @@ -12,6 +10,8 @@ from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.restore_state import RestoreEntity +from . import CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN + _LOGGER = logging.getLogger(__name__) CONF_COIL = 'coil' diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index e4d468e21558d2..2c605fb4b0ddd6 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -5,8 +5,8 @@ https://home-assistant.io/components/mqtt/ """ import asyncio -import inspect from functools import partial, wraps +import inspect from itertools import groupby import json import logging @@ -22,6 +22,7 @@ import voluptuous as vol from homeassistant import config_entries +from homeassistant.components import websocket_api from homeassistant.const import ( CONF_DEVICE, CONF_NAME, CONF_PASSWORD, CONF_PAYLOAD, CONF_PORT, CONF_PROTOCOL, CONF_USERNAME, CONF_VALUE_TEMPLATE, @@ -37,7 +38,6 @@ from homeassistant.util.async_ import ( run_callback_threadsafe, run_coroutine_threadsafe) from homeassistant.util.logging import catch_log_exception -from homeassistant.components import websocket_api # Loading the config flow file will register the flow from . import config_flow # noqa pylint: disable=unused-import @@ -1012,7 +1012,7 @@ async def async_added_to_hass(self) -> None: await super().async_added_to_hass() from homeassistant.helpers.dispatcher import async_dispatcher_connect - from homeassistant.components.mqtt.discovery import ( + from .discovery import ( MQTT_DISCOVERY_UPDATED, clear_discovery_hash) @callback diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index c350b32b4fff44..9498b59759097d 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -11,12 +11,6 @@ from homeassistant.components import mqtt import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, - CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( CONF_CODE, CONF_DEVICE, CONF_NAME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, @@ -26,6 +20,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, + CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) CONF_CODE_ARM_REQUIRED = 'code_arm_required' diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index f2a93d06f8ebf7..7532f3053281fb 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -11,12 +11,6 @@ from homeassistant.components import binary_sensor, mqtt from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, BinarySensorDevice) -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, CONF_UNIQUE_ID, - MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, - MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( CONF_DEVICE, CONF_DEVICE_CLASS, CONF_FORCE_UPDATE, CONF_NAME, CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, CONF_VALUE_TEMPLATE) @@ -26,6 +20,12 @@ import homeassistant.helpers.event as evt from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, CONF_UNIQUE_ID, + MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, + MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'MQTT Binary sensor' diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index ca41f3c422500f..889e5533403316 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -12,17 +12,17 @@ from homeassistant.components import camera, mqtt from homeassistant.components.camera import PLATFORM_SCHEMA, Camera -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttDiscoveryUpdate, - subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import CONF_NAME from homeassistant.core import callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttDiscoveryUpdate, + subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) CONF_TOPIC = 'topic' diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index ae8474379328e0..a9c23d27e11d0e 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -10,20 +10,13 @@ from homeassistant.components import climate, mqtt from homeassistant.components.climate import ( - ClimateDevice, PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA) + PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA, ClimateDevice) from homeassistant.components.climate.const import ( - ATTR_OPERATION_MODE, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, - STATE_AUTO, STATE_COOL, - STATE_DRY, STATE_FAN_ONLY, STATE_HEAT, SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, - SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, SUPPORT_OPERATION_MODE, - SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE) + ATTR_OPERATION_MODE, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, STATE_AUTO, + STATE_COOL, STATE_DRY, STATE_FAN_ONLY, STATE_HEAT, SUPPORT_AUX_HEAT, + SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, - CONF_UNIQUE_ID, MQTT_BASE_PLATFORM_SCHEMA, MqttAttributes, - MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( ATTR_TEMPERATURE, CONF_DEVICE, CONF_NAME, CONF_VALUE_TEMPLATE, STATE_OFF, STATE_ON) @@ -32,6 +25,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + CONF_UNIQUE_ID, MQTT_BASE_PLATFORM_SCHEMA, MqttAttributes, + MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 37222cbe86800e..8116421ac10625 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -13,12 +13,6 @@ ATTR_POSITION, ATTR_TILT_POSITION, DEVICE_CLASSES_SCHEMA, SUPPORT_CLOSE, SUPPORT_CLOSE_TILT, SUPPORT_OPEN, SUPPORT_OPEN_TILT, SUPPORT_SET_POSITION, SUPPORT_SET_TILT_POSITION, SUPPORT_STOP, SUPPORT_STOP_TILT, CoverDevice) -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, - CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( CONF_DEVICE, CONF_DEVICE_CLASS, CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE, STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN) @@ -28,6 +22,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, + CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index bf55d955ce1ffd..0f22ed150ca630 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -9,12 +9,13 @@ import voluptuous as vol from homeassistant.components import mqtt -from homeassistant.core import callback -from homeassistant.const import CONF_DEVICES -from homeassistant.components.mqtt import CONF_QOS from homeassistant.components.device_tracker import PLATFORM_SCHEMA +from homeassistant.const import CONF_DEVICES +from homeassistant.core import callback import homeassistant.helpers.config_validation as cv +from . import CONF_QOS + DEPENDENCIES = ['mqtt'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 745e54d0ed74df..cb87a208b4fef3 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -10,12 +10,13 @@ import re from homeassistant.components import mqtt -from homeassistant.components.mqtt import ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC from homeassistant.const import CONF_DEVICE, CONF_PLATFORM from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import HomeAssistantType +from . import ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC + _LOGGER = logging.getLogger(__name__) TOPIC_MATCHER = re.compile( diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 7c9f816eff7ceb..b8bff6088d8cca 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -12,12 +12,6 @@ from homeassistant.components.fan import ( ATTR_SPEED, SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity) -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, - CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( CONF_DEVICE, CONF_NAME, CONF_OPTIMISTIC, CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, CONF_STATE, STATE_OFF, STATE_ON) @@ -26,6 +20,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, + CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index d9adc37d79a836..ee459d2174f001 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -10,12 +10,6 @@ from homeassistant.components import lock, mqtt from homeassistant.components.lock import LockDevice -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, - CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.const import ( CONF_DEVICE, CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE) from homeassistant.core import callback @@ -23,6 +17,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, + CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) CONF_PAYLOAD_LOCK = 'payload_lock' diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index c6ef3344fcf81d..aa8d5e2a31e348 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -12,12 +12,6 @@ import voluptuous as vol from homeassistant.components import mqtt, sensor -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, CONF_UNIQUE_ID, - MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, - MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.components.sensor import DEVICE_CLASSES_SCHEMA from homeassistant.const import ( CONF_DEVICE, CONF_DEVICE_CLASS, CONF_FORCE_UPDATE, CONF_ICON, CONF_NAME, @@ -30,6 +24,12 @@ from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.util import dt as dt_util +from . import ( + ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, CONF_UNIQUE_ID, + MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, + MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) CONF_EXPIRE_AFTER = 'expire_after' diff --git a/homeassistant/components/mqtt/subscription.py b/homeassistant/components/mqtt/subscription.py index e4563fc377e000..e159132d5609cd 100644 --- a/homeassistant/components/mqtt/subscription.py +++ b/homeassistant/components/mqtt/subscription.py @@ -10,10 +10,11 @@ import attr from homeassistant.components import mqtt -from homeassistant.components.mqtt import DEFAULT_QOS, MessageCallbackType from homeassistant.helpers.typing import HomeAssistantType from homeassistant.loader import bind_hass +from . import DEFAULT_QOS, MessageCallbackType + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index de7da6b724975b..4847afd80c90e6 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -9,12 +9,6 @@ import voluptuous as vol from homeassistant.components import mqtt, switch -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, - CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.components.switch import SwitchDevice from homeassistant.const import ( CONF_DEVICE, CONF_ICON, CONF_NAME, CONF_OPTIMISTIC, CONF_PAYLOAD_OFF, @@ -25,6 +19,12 @@ from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from . import ( + ATTR_DISCOVERY_HASH, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, + CONF_STATE_TOPIC, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index eb7e78b6254b20..efa00821c1ba62 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -9,11 +9,6 @@ import voluptuous as vol from homeassistant.components import mqtt -from homeassistant.components.mqtt import ( - ATTR_DISCOVERY_HASH, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, - MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) -from homeassistant.components.mqtt.discovery import ( - MQTT_DISCOVERY_NEW, clear_discovery_hash) from homeassistant.components.vacuum import ( DOMAIN, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, @@ -25,6 +20,11 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.icon import icon_for_battery_level +from . import ( + ATTR_DISCOVERY_HASH, CONF_UNIQUE_ID, MqttAttributes, MqttAvailability, + MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription) +from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mychevy/binary_sensor.py b/homeassistant/components/mychevy/binary_sensor.py index 67f12a14359ddb..a2435d596be2ff 100644 --- a/homeassistant/components/mychevy/binary_sensor.py +++ b/homeassistant/components/mychevy/binary_sensor.py @@ -1,14 +1,13 @@ """Support for MyChevy binary sensors.""" import logging -from homeassistant.components.mychevy import ( - EVBinarySensorConfig, DOMAIN as MYCHEVY_DOMAIN, UPDATE_TOPIC -) from homeassistant.components.binary_sensor import ( ENTITY_ID_FORMAT, BinarySensorDevice) from homeassistant.core import callback from homeassistant.util import slugify +from . import DOMAIN as MYCHEVY_DOMAIN, UPDATE_TOPIC, EVBinarySensorConfig + _LOGGER = logging.getLogger(__name__) SENSORS = [ diff --git a/homeassistant/components/mychevy/sensor.py b/homeassistant/components/mychevy/sensor.py index c7d140e0c4c76a..42b27df62993db 100644 --- a/homeassistant/components/mychevy/sensor.py +++ b/homeassistant/components/mychevy/sensor.py @@ -1,16 +1,16 @@ """Support for MyChevy sensors.""" import logging -from homeassistant.components.mychevy import ( - EVSensorConfig, DOMAIN as MYCHEVY_DOMAIN, MYCHEVY_ERROR, MYCHEVY_SUCCESS, - UPDATE_TOPIC, ERROR_TOPIC -) from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.core import callback from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.util import slugify +from . import ( + DOMAIN as MYCHEVY_DOMAIN, ERROR_TOPIC, MYCHEVY_ERROR, MYCHEVY_SUCCESS, + UPDATE_TOPIC, EVSensorConfig) + _LOGGER = logging.getLogger(__name__) BATTERY_SENSOR = "batteryLevel" diff --git a/homeassistant/components/neato/camera.py b/homeassistant/components/neato/camera.py index 530aa8fc6f1970..f8106c3e645ebc 100644 --- a/homeassistant/components/neato/camera.py +++ b/homeassistant/components/neato/camera.py @@ -1,10 +1,10 @@ """Support for loading picture from Neato.""" +from datetime import timedelta import logging -from datetime import timedelta from homeassistant.components.camera import Camera -from homeassistant.components.neato import ( - NEATO_MAP_DATA, NEATO_ROBOTS, NEATO_LOGIN) + +from . import NEATO_LOGIN, NEATO_MAP_DATA, NEATO_ROBOTS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/neato/switch.py b/homeassistant/components/neato/switch.py index fcc72762b8d059..ea60f9492e22a7 100644 --- a/homeassistant/components/neato/switch.py +++ b/homeassistant/components/neato/switch.py @@ -1,10 +1,13 @@ """Support for Neato Connected Vacuums switches.""" -import logging from datetime import timedelta +import logging + import requests + from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers.entity import ToggleEntity -from homeassistant.components.neato import NEATO_ROBOTS, NEATO_LOGIN + +from . import NEATO_LOGIN, NEATO_ROBOTS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/neato/vacuum.py b/homeassistant/components/neato/vacuum.py index 2f2f39049473eb..3575301ea97e6b 100644 --- a/homeassistant/components/neato/vacuum.py +++ b/homeassistant/components/neato/vacuum.py @@ -1,22 +1,23 @@ """Support for Neato Connected Vacuums.""" -import logging from datetime import timedelta +import logging + import requests import voluptuous as vol -from homeassistant.const import (ATTR_ENTITY_ID) from homeassistant.components.vacuum import ( - StateVacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, - SUPPORT_STATE, SUPPORT_STOP, SUPPORT_START, STATE_IDLE, - STATE_PAUSED, STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR, - SUPPORT_MAP, ATTR_STATUS, ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON, - SUPPORT_LOCATE, SUPPORT_CLEAN_SPOT, DOMAIN) -from homeassistant.components.neato import ( - NEATO_ROBOTS, NEATO_LOGIN, NEATO_MAP_DATA, ACTION, ERRORS, MODE, ALERTS, - NEATO_PERSISTENT_MAPS) - -from homeassistant.helpers.service import extract_entity_ids + ATTR_BATTERY_ICON, ATTR_BATTERY_LEVEL, ATTR_STATUS, DOMAIN, STATE_CLEANING, + STATE_DOCKED, STATE_ERROR, STATE_IDLE, STATE_PAUSED, STATE_RETURNING, + SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_LOCATE, SUPPORT_MAP, + SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_START, SUPPORT_STATE, + SUPPORT_STOP, StateVacuumDevice) +from homeassistant.const import ATTR_ENTITY_ID import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.service import extract_entity_ids + +from . import ( + ACTION, ALERTS, ERRORS, MODE, NEATO_LOGIN, NEATO_MAP_DATA, + NEATO_PERSISTENT_MAPS, NEATO_ROBOTS) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ness_alarm/alarm_control_panel.py b/homeassistant/components/ness_alarm/alarm_control_panel.py index ee3a0c213cba3e..f77b534980f4fe 100644 --- a/homeassistant/components/ness_alarm/alarm_control_panel.py +++ b/homeassistant/components/ness_alarm/alarm_control_panel.py @@ -8,14 +8,14 @@ import logging import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.ness_alarm import ( - DATA_NESS, SIGNAL_ARMING_STATE_CHANGED) from homeassistant.const import ( - STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING, - STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_DISARMED) + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING, STATE_ALARM_DISARMED, + STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DATA_NESS, SIGNAL_ARMING_STATE_CHANGED + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ness_alarm'] diff --git a/homeassistant/components/ness_alarm/binary_sensor.py b/homeassistant/components/ness_alarm/binary_sensor.py index 9f1479efd6983f..7b684f74aa1253 100644 --- a/homeassistant/components/ness_alarm/binary_sensor.py +++ b/homeassistant/components/ness_alarm/binary_sensor.py @@ -7,12 +7,13 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.ness_alarm import ( - CONF_ZONES, CONF_ZONE_TYPE, CONF_ZONE_NAME, CONF_ZONE_ID, - SIGNAL_ZONE_CHANGED, ZoneChangedData) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import ( + CONF_ZONE_ID, CONF_ZONE_NAME, CONF_ZONE_TYPE, CONF_ZONES, + SIGNAL_ZONE_CHANGED, ZoneChangedData) + DEPENDENCIES = ['ness_alarm'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/nest/binary_sensor.py b/homeassistant/components/nest/binary_sensor.py index 1077fdb073e7ba..aa56bfbf29d50c 100644 --- a/homeassistant/components/nest/binary_sensor.py +++ b/homeassistant/components/nest/binary_sensor.py @@ -3,10 +3,11 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.nest import ( - DATA_NEST, DATA_NEST_CONFIG, CONF_BINARY_SENSORS, NestSensorDevice) from homeassistant.const import CONF_MONITORED_CONDITIONS +from . import ( + CONF_BINARY_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['nest'] diff --git a/homeassistant/components/nest/climate.py b/homeassistant/components/nest/climate.py index 88b6cbbbeb00e2..cd9a7cb71b634d 100644 --- a/homeassistant/components/nest/climate.py +++ b/homeassistant/components/nest/climate.py @@ -3,20 +3,19 @@ import voluptuous as vol -from homeassistant.components.nest import ( - DATA_NEST, SIGNAL_NEST_UPDATE, DOMAIN as NEST_DOMAIN) -from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA +from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_ECO, - ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - SUPPORT_TARGET_TEMPERATURE, - SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW, - SUPPORT_OPERATION_MODE, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE) + ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, STATE_AUTO, STATE_COOL, + STATE_ECO, STATE_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, + SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) from homeassistant.const import ( - ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT, - CONF_SCAN_INTERVAL, STATE_ON, STATE_OFF) + ATTR_TEMPERATURE, CONF_SCAN_INTERVAL, STATE_OFF, STATE_ON, TEMP_CELSIUS, + TEMP_FAHRENHEIT) from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DATA_NEST, DOMAIN as NEST_DOMAIN, SIGNAL_NEST_UPDATE + DEPENDENCIES = ['nest'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py index bde3f681c2b520..ecae83e303c2a9 100644 --- a/homeassistant/components/nest/sensor.py +++ b/homeassistant/components/nest/sensor.py @@ -1,13 +1,12 @@ """Support for Nest Thermostat sensors.""" import logging -from homeassistant.components.climate.const import ( - STATE_COOL, STATE_HEAT) -from homeassistant.components.nest import ( - DATA_NEST, DATA_NEST_CONFIG, CONF_SENSORS, NestSensorDevice) +from homeassistant.components.climate.const import STATE_COOL, STATE_HEAT from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_MONITORED_CONDITIONS, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, STATE_OFF) + CONF_MONITORED_CONDITIONS, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, + STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) + +from . import CONF_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice DEPENDENCIES = ['nest'] diff --git a/homeassistant/components/netatmo/binary_sensor.py b/homeassistant/components/netatmo/binary_sensor.py index 7986010ef64693..a11ce6bddf710a 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -4,11 +4,12 @@ import voluptuous as vol from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.netatmo import CameraData + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import CONF_TIMEOUT from homeassistant.helpers import config_validation as cv +from . import CameraData + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['netatmo'] diff --git a/homeassistant/components/netatmo/camera.py b/homeassistant/components/netatmo/camera.py index 57d30d6cbc98f0..513852cde23990 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -4,11 +4,12 @@ import requests import voluptuous as vol +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.const import CONF_VERIFY_SSL -from homeassistant.components.netatmo import CameraData -from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) from homeassistant.helpers import config_validation as cv +from . import CameraData + DEPENDENCIES = ['netatmo'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/nissan_leaf/binary_sensor.py b/homeassistant/components/nissan_leaf/binary_sensor.py index 2397405ec201fd..5c71cf1fc513a9 100644 --- a/homeassistant/components/nissan_leaf/binary_sensor.py +++ b/homeassistant/components/nissan_leaf/binary_sensor.py @@ -1,10 +1,10 @@ """Plugged In Status Support for the Nissan Leaf.""" import logging -from homeassistant.components.nissan_leaf import ( - DATA_CHARGING, DATA_LEAF, DATA_PLUGGED_IN, LeafEntity) from homeassistant.components.binary_sensor import BinarySensorDevice +from . import DATA_CHARGING, DATA_LEAF, DATA_PLUGGED_IN, LeafEntity + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['nissan_leaf'] diff --git a/homeassistant/components/nissan_leaf/device_tracker.py b/homeassistant/components/nissan_leaf/device_tracker.py index 1ca7fceb911882..95f6fcdcaf16c0 100644 --- a/homeassistant/components/nissan_leaf/device_tracker.py +++ b/homeassistant/components/nissan_leaf/device_tracker.py @@ -1,11 +1,11 @@ """Support for tracking a Nissan Leaf.""" import logging -from homeassistant.components.nissan_leaf import ( - DATA_LEAF, DATA_LOCATION, SIGNAL_UPDATE_LEAF) from homeassistant.helpers.dispatcher import dispatcher_connect from homeassistant.util import slugify +from . import DATA_LEAF, DATA_LOCATION, SIGNAL_UPDATE_LEAF + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['nissan_leaf'] diff --git a/homeassistant/components/nissan_leaf/sensor.py b/homeassistant/components/nissan_leaf/sensor.py index f6206f1f4efc5a..682f482b4888f2 100644 --- a/homeassistant/components/nissan_leaf/sensor.py +++ b/homeassistant/components/nissan_leaf/sensor.py @@ -1,14 +1,15 @@ """Battery Charge and Range Support for the Nissan Leaf.""" import logging -from homeassistant.components.nissan_leaf import ( - DATA_BATTERY, DATA_CHARGING, DATA_LEAF, DATA_RANGE_AC, DATA_RANGE_AC_OFF, - LeafEntity) from homeassistant.const import DEVICE_CLASS_BATTERY from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.util.distance import LENGTH_KILOMETERS, LENGTH_MILES from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM +from . import ( + DATA_BATTERY, DATA_CHARGING, DATA_LEAF, DATA_RANGE_AC, DATA_RANGE_AC_OFF, + LeafEntity) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['nissan_leaf'] diff --git a/homeassistant/components/nissan_leaf/switch.py b/homeassistant/components/nissan_leaf/switch.py index 60b9a6630cd638..e6d72103a6c6c4 100644 --- a/homeassistant/components/nissan_leaf/switch.py +++ b/homeassistant/components/nissan_leaf/switch.py @@ -1,10 +1,10 @@ """Charge and Climate Control Support for the Nissan Leaf.""" import logging -from homeassistant.components.nissan_leaf import ( - DATA_CLIMATE, DATA_LEAF, LeafEntity) from homeassistant.helpers.entity import ToggleEntity +from . import DATA_CLIMATE, DATA_LEAF, LeafEntity + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['nissan_leaf'] diff --git a/homeassistant/components/notify/apns.py b/homeassistant/components/notify/apns.py index 8fabfc3aefb505..3f5403f0c136fa 100644 --- a/homeassistant/components/notify/apns.py +++ b/homeassistant/components/notify/apns.py @@ -9,13 +9,14 @@ import voluptuous as vol -from homeassistant.helpers.event import track_state_change from homeassistant.config import load_yaml_config_file -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_DATA, BaseNotificationService, DOMAIN, PLATFORM_SCHEMA) -from homeassistant.const import CONF_NAME, CONF_PLATFORM, ATTR_NAME -import homeassistant.helpers.config_validation as cv +from homeassistant.const import ATTR_NAME, CONF_NAME, CONF_PLATFORM from homeassistant.helpers import template as template_helper +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import track_state_change + +from . import ( + ATTR_DATA, ATTR_TARGET, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['apns2==0.3.0'] diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index 17df1ba8f5aa0c..e605f82c3f15aa 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -4,19 +4,18 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.aws_lambda/ """ -import logging -import json import base64 +import json +import logging import voluptuous as vol -from homeassistant.const import ( - CONF_PLATFORM, CONF_NAME) -from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) +from homeassistant.const import CONF_NAME, CONF_PLATFORM import homeassistant.helpers.config_validation as cv from homeassistant.helpers.json import JSONEncoder +from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService + REQUIREMENTS = ['boto3==1.9.16'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/aws_sns.py b/homeassistant/components/notify/aws_sns.py index 065898bcb85de1..9363576fc1aced 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/notify/aws_sns.py @@ -4,18 +4,18 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.aws_sns/ """ -import logging import json +import logging import voluptuous as vol -from homeassistant.const import ( - CONF_PLATFORM, CONF_NAME) -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_TARGET, PLATFORM_SCHEMA, - BaseNotificationService) +from homeassistant.const import CONF_NAME, CONF_PLATFORM import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ["boto3==1.9.16"] diff --git a/homeassistant/components/notify/aws_sqs.py b/homeassistant/components/notify/aws_sqs.py index 78e71bde97a890..ed22147cfedc3a 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/notify/aws_sqs.py @@ -4,17 +4,16 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.aws_sqs/ """ -import logging import json +import logging import voluptuous as vol -from homeassistant.const import ( - CONF_PLATFORM, CONF_NAME) -from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) +from homeassistant.const import CONF_NAME, CONF_PLATFORM import homeassistant.helpers.config_validation as cv +from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ["boto3==1.9.16"] diff --git a/homeassistant/components/notify/ciscospark.py b/homeassistant/components/notify/ciscospark.py index e83e0e9024fff8..a33e9432e927e8 100644 --- a/homeassistant/components/notify/ciscospark.py +++ b/homeassistant/components/notify/ciscospark.py @@ -8,11 +8,11 @@ import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService, ATTR_TITLE) -from homeassistant.const import (CONF_TOKEN) +from homeassistant.const import CONF_TOKEN import homeassistant.helpers.config_validation as cv +from . import ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService + REQUIREMENTS = ['ciscosparkapi==0.4.2'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/clickatell.py b/homeassistant/components/notify/clickatell.py index 6af2b4551297f7..559232e25558fe 100644 --- a/homeassistant/components/notify/clickatell.py +++ b/homeassistant/components/notify/clickatell.py @@ -9,10 +9,10 @@ import requests import voluptuous as vol +from homeassistant.const import CONF_API_KEY, CONF_RECIPIENT import homeassistant.helpers.config_validation as cv -from homeassistant.const import (CONF_API_KEY, CONF_RECIPIENT) -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) + +from . import PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/clicksend.py b/homeassistant/components/notify/clicksend.py index faf30ac7cc667f..6f10ac707349c3 100644 --- a/homeassistant/components/notify/clicksend.py +++ b/homeassistant/components/notify/clicksend.py @@ -7,16 +7,16 @@ import json import logging +from aiohttp.hdrs import CONTENT_TYPE import requests import voluptuous as vol -from aiohttp.hdrs import CONTENT_TYPE -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import ( CONF_API_KEY, CONF_RECIPIENT, CONF_SENDER, CONF_USERNAME, CONTENT_TYPE_JSON) +import homeassistant.helpers.config_validation as cv + +from . import PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/clicksend_tts.py b/homeassistant/components/notify/clicksend_tts.py index c60e02ece1ac9b..1481fea1783cdd 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/notify/clicksend_tts.py @@ -9,15 +9,15 @@ import json import logging +from aiohttp.hdrs import CONTENT_TYPE import requests import voluptuous as vol -from aiohttp.hdrs import CONTENT_TYPE -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import ( - CONF_API_KEY, CONF_USERNAME, CONF_RECIPIENT, CONTENT_TYPE_JSON) + CONF_API_KEY, CONF_RECIPIENT, CONF_USERNAME, CONTENT_TYPE_JSON) +import homeassistant.helpers.config_validation as cv + +from . import PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/command_line.py b/homeassistant/components/notify/command_line.py index 4a7483d6e4d7aa..6a81dace28803d 100644 --- a/homeassistant/components/notify/command_line.py +++ b/homeassistant/components/notify/command_line.py @@ -9,11 +9,11 @@ import voluptuous as vol -from homeassistant.const import (CONF_COMMAND, CONF_NAME) -from homeassistant.components.notify import ( - BaseNotificationService, PLATFORM_SCHEMA) +from homeassistant.const import CONF_COMMAND, CONF_NAME import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService + _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/notify/demo.py b/homeassistant/components/notify/demo.py index 5b8e1f1688f099..9cb6160901730e 100644 --- a/homeassistant/components/notify/demo.py +++ b/homeassistant/components/notify/demo.py @@ -4,7 +4,7 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.notify import BaseNotificationService +from . import BaseNotificationService EVENT_NOTIFY = "notify" diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/notify/discord.py index e9f0d5cec28dfa..f98c136b3f975e 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/notify/discord.py @@ -8,10 +8,10 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService, ATTR_TARGET, ATTR_DATA) from homeassistant.const import CONF_TOKEN +import homeassistant.helpers.config_validation as cv + +from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/facebook.py b/homeassistant/components/notify/facebook.py index b73f845ea175cd..b642a7b932b483 100644 --- a/homeassistant/components/notify/facebook.py +++ b/homeassistant/components/notify/facebook.py @@ -11,11 +11,11 @@ import requests import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONTENT_TYPE_JSON import homeassistant.helpers.config_validation as cv +from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService + _LOGGER = logging.getLogger(__name__) CONF_PAGE_ACCESS_TOKEN = 'page_access_token' diff --git a/homeassistant/components/notify/file.py b/homeassistant/components/notify/file.py index 749a6d4b330b3f..98f6da66e3d602 100644 --- a/homeassistant/components/notify/file.py +++ b/homeassistant/components/notify/file.py @@ -9,11 +9,12 @@ import voluptuous as vol -import homeassistant.util.dt as dt_util -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_FILENAME import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util + +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) CONF_TIMESTAMP = 'timestamp' diff --git a/homeassistant/components/notify/flock.py b/homeassistant/components/notify/flock.py index d26f629809f8b3..16f38bbbb63276 100644 --- a/homeassistant/components/notify/flock.py +++ b/homeassistant/components/notify/flock.py @@ -10,12 +10,12 @@ import async_timeout import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService + _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://api.flock.com/hooks/sendMessage/' diff --git a/homeassistant/components/notify/free_mobile.py b/homeassistant/components/notify/free_mobile.py index a27d0495193dcf..fb7829105fce75 100644 --- a/homeassistant/components/notify/free_mobile.py +++ b/homeassistant/components/notify/free_mobile.py @@ -8,11 +8,11 @@ import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ACCESS_TOKEN, CONF_USERNAME import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService + REQUIREMENTS = ['freesms==0.1.2'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/gntp.py b/homeassistant/components/notify/gntp.py index 1a2b65f958f7de..4560eb21f31c7f 100644 --- a/homeassistant/components/notify/gntp.py +++ b/homeassistant/components/notify/gntp.py @@ -9,11 +9,12 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_PASSWORD, CONF_PORT import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) + REQUIREMENTS = ['gntp==1.0.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/group.py b/homeassistant/components/notify/group.py index 5d25c2d815e1b6..85d4d53e3bed3b 100644 --- a/homeassistant/components/notify/group.py +++ b/homeassistant/components/notify/group.py @@ -8,13 +8,15 @@ from collections.abc import Mapping from copy import deepcopy import logging + import voluptuous as vol from homeassistant.const import ATTR_SERVICE -from homeassistant.components.notify import ( - DOMAIN, ATTR_MESSAGE, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_MESSAGE, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) + _LOGGER = logging.getLogger(__name__) CONF_SERVICES = 'services' diff --git a/homeassistant/components/notify/hipchat.py b/homeassistant/components/notify/hipchat.py index 344827c00b4a26..8ce7b8b120e8b6 100644 --- a/homeassistant/components/notify/hipchat.py +++ b/homeassistant/components/notify/hipchat.py @@ -8,10 +8,10 @@ import voluptuous as vol +from homeassistant.const import CONF_HOST, CONF_ROOM, CONF_TOKEN import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_TOKEN, CONF_HOST, CONF_ROOM + +from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService REQUIREMENTS = ['hipnotify==1.0.8'] diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index 17f7e3163573e6..f9bf36e61f0936 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -5,9 +5,9 @@ https://home-assistant.io/components/notify.html5/ """ import datetime +from functools import partial import json import logging -from functools import partial import time import uuid @@ -15,18 +15,19 @@ import voluptuous as vol from voluptuous.humanize import humanize_error -from homeassistant.util.json import load_json, save_json -from homeassistant.exceptions import HomeAssistantError from homeassistant.components import websocket_api from homeassistant.components.frontend import add_manifest_json_key from homeassistant.components.http import HomeAssistantView -from homeassistant.components.notify import ( - ATTR_DATA, ATTR_TITLE, ATTR_TARGET, PLATFORM_SCHEMA, ATTR_TITLE_DEFAULT, - BaseNotificationService, DOMAIN) from homeassistant.const import ( - URL_ROOT, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_INTERNAL_SERVER_ERROR) + HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, HTTP_UNAUTHORIZED, URL_ROOT) +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.util import ensure_unique_string +from homeassistant.util.json import load_json, save_json + +from . import ( + ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, DOMAIN, + PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['pywebpush==1.6.0'] diff --git a/homeassistant/components/notify/kodi.py b/homeassistant/components/notify/kodi.py index 50d2246cd29c4f..2dd33bf8990603 100644 --- a/homeassistant/components/notify/kodi.py +++ b/homeassistant/components/notify/kodi.py @@ -10,14 +10,15 @@ import voluptuous as vol from homeassistant.const import ( - ATTR_ICON, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD, - CONF_PROXY_SSL) -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_DATA, PLATFORM_SCHEMA, - BaseNotificationService) + ATTR_ICON, CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_PROXY_SSL, + CONF_USERNAME) from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + REQUIREMENTS = ['jsonrpc-async==0.6'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/lannouncer.py b/homeassistant/components/notify/lannouncer.py index 5677f38b06ce59..5c975e8422f987 100644 --- a/homeassistant/components/notify/lannouncer.py +++ b/homeassistant/components/notify/lannouncer.py @@ -5,16 +5,16 @@ https://home-assistant.io/components/notify.lannouncer/ """ import logging - -from urllib.parse import urlencode import socket +from urllib.parse import urlencode + import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, ATTR_DATA, BaseNotificationService) -from homeassistant.const import (CONF_HOST, CONF_PORT) +from homeassistant.const import CONF_HOST, CONF_PORT import homeassistant.helpers.config_validation as cv +from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService + ATTR_METHOD = 'method' ATTR_METHOD_DEFAULT = 'speak' ATTR_METHOD_ALLOWED = ['speak', 'alarm'] diff --git a/homeassistant/components/notify/llamalab_automate.py b/homeassistant/components/notify/llamalab_automate.py index 0ddcb450bcf7d0..d3689dbbd818e9 100644 --- a/homeassistant/components/notify/llamalab_automate.py +++ b/homeassistant/components/notify/llamalab_automate.py @@ -5,14 +5,14 @@ https://home-assistant.io/components/notify.llamalab_automate/ """ import logging + import requests import voluptuous as vol -from homeassistant.components.notify import ( - BaseNotificationService, PLATFORM_SCHEMA) from homeassistant.const import CONF_API_KEY, CONF_DEVICE from homeassistant.helpers import config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://llamalab.com/automate/cloud/message' diff --git a/homeassistant/components/notify/mastodon.py b/homeassistant/components/notify/mastodon.py index 095e3f98ff91e3..59c787bc026bc3 100644 --- a/homeassistant/components/notify/mastodon.py +++ b/homeassistant/components/notify/mastodon.py @@ -8,11 +8,11 @@ import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ACCESS_TOKEN import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService + REQUIREMENTS = ['Mastodon.py==1.3.1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/message_bird.py b/homeassistant/components/notify/message_bird.py index fa747ccba88dee..c45d153d813a0e 100644 --- a/homeassistant/components/notify/message_bird.py +++ b/homeassistant/components/notify/message_bird.py @@ -8,10 +8,10 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_API_KEY, CONF_SENDER +import homeassistant.helpers.config_validation as cv + +from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService REQUIREMENTS = ['messagebird==1.2.0'] diff --git a/homeassistant/components/notify/mycroft.py b/homeassistant/components/notify/mycroft.py index 1fd22c5c42b5ca..e9cd44d5d06b07 100644 --- a/homeassistant/components/notify/mycroft.py +++ b/homeassistant/components/notify/mycroft.py @@ -6,8 +6,7 @@ """ import logging - -from homeassistant.components.notify import BaseNotificationService +from . import BaseNotificationService DEPENDENCIES = ['mycroft'] diff --git a/homeassistant/components/notify/nfandroidtv.py b/homeassistant/components/notify/nfandroidtv.py index f99d97574b4eb2..8127b4b3e967a3 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/notify/nfandroidtv.py @@ -4,21 +4,21 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.nfandroidtv/ """ -import logging -import io import base64 +import io +import logging import requests -from requests.auth import HTTPBasicAuth -from requests.auth import HTTPDigestAuth +from requests.auth import HTTPBasicAuth, HTTPDigestAuth import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_DATA, BaseNotificationService, - PLATFORM_SCHEMA) from homeassistant.const import CONF_TIMEOUT import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + _LOGGER = logging.getLogger(__name__) CONF_IP = 'host' diff --git a/homeassistant/components/notify/prowl.py b/homeassistant/components/notify/prowl.py index f0741766a707f0..27ce8d0fb7a7ce 100644 --- a/homeassistant/components/notify/prowl.py +++ b/homeassistant/components/notify/prowl.py @@ -4,18 +4,19 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.prowl/ """ -import logging import asyncio +import logging import async_timeout import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_DATA, PLATFORM_SCHEMA, - BaseNotificationService) from homeassistant.const import CONF_API_KEY -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://api.prowlapp.com/publicapi/' diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/notify/pushbullet.py index a94cf4f105528d..505d6f4e1c144f 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -9,12 +9,13 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, - BaseNotificationService) from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + REQUIREMENTS = ['pushbullet.py==0.11.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/pushetta.py b/homeassistant/components/notify/pushetta.py index a29cd5871054ae..5db67177548686 100644 --- a/homeassistant/components/notify/pushetta.py +++ b/homeassistant/components/notify/pushetta.py @@ -8,11 +8,12 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) + _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pushetta==1.0.15'] diff --git a/homeassistant/components/notify/pushover.py b/homeassistant/components/notify/pushover.py index b249ca804b302e..372ea361f13bde 100644 --- a/homeassistant/components/notify/pushover.py +++ b/homeassistant/components/notify/pushover.py @@ -8,12 +8,13 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_TARGET, ATTR_DATA, - BaseNotificationService, PLATFORM_SCHEMA) from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + REQUIREMENTS = ['python-pushover==0.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/pushsafer.py b/homeassistant/components/notify/pushsafer.py index 94dc08a8113857..9a3308ff21e002 100644 --- a/homeassistant/components/notify/pushsafer.py +++ b/homeassistant/components/notify/pushsafer.py @@ -4,19 +4,20 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.pushsafer/ """ -import logging import base64 +import logging import mimetypes import requests from requests.auth import HTTPBasicAuth import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_TARGET, ATTR_DATA, - PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://www.pushsafer.com/api' _ALLOWED_IMAGES = ['image/gif', 'image/jpeg', 'image/png'] diff --git a/homeassistant/components/notify/rest.py b/homeassistant/components/notify/rest.py index 469319a0f3e760..eec2ea4aa37bcb 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/notify/rest.py @@ -9,16 +9,16 @@ import requests import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService, - PLATFORM_SCHEMA) -from homeassistant.const import (CONF_AUTHENTICATION, CONF_HEADERS, - CONF_METHOD, CONF_NAME, CONF_PASSWORD, - CONF_RESOURCE, CONF_USERNAME, CONF_VERIFY_SSL, - HTTP_BASIC_AUTHENTICATION, - HTTP_DIGEST_AUTHENTICATION) +from homeassistant.const import ( + CONF_AUTHENTICATION, CONF_HEADERS, CONF_METHOD, CONF_NAME, CONF_PASSWORD, + CONF_RESOURCE, CONF_USERNAME, CONF_VERIFY_SSL, HTTP_BASIC_AUTHENTICATION, + HTTP_DIGEST_AUTHENTICATION) import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, + BaseNotificationService) + CONF_DATA = 'data' CONF_DATA_TEMPLATE = 'data_template' CONF_MESSAGE_PARAMETER_NAME = 'message_param_name' diff --git a/homeassistant/components/notify/rocketchat.py b/homeassistant/components/notify/rocketchat.py index e9b481b1cf3c14..116c32993d829b 100644 --- a/homeassistant/components/notify/rocketchat.py +++ b/homeassistant/components/notify/rocketchat.py @@ -8,11 +8,11 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.const import ( - CONF_URL, CONF_USERNAME, CONF_PASSWORD, CONF_ROOM) -from homeassistant.components.notify import ( - ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) + CONF_PASSWORD, CONF_ROOM, CONF_URL, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv + +from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService REQUIREMENTS = ['rocketchat-API==0.6.1'] diff --git a/homeassistant/components/notify/sendgrid.py b/homeassistant/components/notify/sendgrid.py index e72dcbbed36029..6bab566bfc7e69 100644 --- a/homeassistant/components/notify/sendgrid.py +++ b/homeassistant/components/notify/sendgrid.py @@ -8,12 +8,13 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import ( - CONF_API_KEY, CONF_SENDER, CONF_RECIPIENT, CONTENT_TYPE_TEXT_PLAIN) + CONF_API_KEY, CONF_RECIPIENT, CONF_SENDER, CONTENT_TYPE_TEXT_PLAIN) import homeassistant.helpers.config_validation as cv +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) + REQUIREMENTS = ['sendgrid==5.6.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/simplepush.py b/homeassistant/components/notify/simplepush.py index 9d5c58fc5b1c5b..1d198b5adec75d 100644 --- a/homeassistant/components/notify/simplepush.py +++ b/homeassistant/components/notify/simplepush.py @@ -8,10 +8,11 @@ import voluptuous as vol +from homeassistant.const import CONF_PASSWORD import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( + +from . import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_PASSWORD REQUIREMENTS = ['simplepush==1.1.4'] diff --git a/homeassistant/components/notify/slack.py b/homeassistant/components/notify/slack.py index 961f671203ffd5..7aee58ed012e20 100644 --- a/homeassistant/components/notify/slack.py +++ b/homeassistant/components/notify/slack.py @@ -7,15 +7,15 @@ import logging import requests -from requests.auth import HTTPBasicAuth -from requests.auth import HTTPDigestAuth +from requests.auth import HTTPBasicAuth, HTTPDigestAuth import voluptuous as vol +from homeassistant.const import CONF_API_KEY, CONF_ICON, CONF_USERNAME import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_TITLE, ATTR_DATA, PLATFORM_SCHEMA, + +from . import ( + ATTR_DATA, ATTR_TARGET, ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import (CONF_API_KEY, CONF_USERNAME, CONF_ICON) REQUIREMENTS = ['slacker==0.12.0'] diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/notify/smtp.py index e3a6f8b85b1069..995aae76cc6d2f 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -4,25 +4,26 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.smtp/ """ -import logging -import smtplib +from email.mime.application import MIMEApplication +from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from email.mime.image import MIMEImage -from email.mime.application import MIMEApplication import email.utils +import logging import os +import smtplib import voluptuous as vol +from homeassistant.const import ( + CONF_PASSWORD, CONF_PORT, CONF_RECIPIENT, CONF_SENDER, CONF_TIMEOUT, + CONF_USERNAME) import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_DATA, PLATFORM_SCHEMA, + +from . import ( + ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import ( - CONF_USERNAME, CONF_PASSWORD, CONF_PORT, CONF_TIMEOUT, - CONF_SENDER, CONF_RECIPIENT) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/stride.py b/homeassistant/components/notify/stride.py index f31e50a588683c..f5f5b52ab67c4c 100644 --- a/homeassistant/components/notify/stride.py +++ b/homeassistant/components/notify/stride.py @@ -8,10 +8,10 @@ import voluptuous as vol +from homeassistant.const import CONF_ROOM, CONF_TOKEN import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_TOKEN, CONF_ROOM + +from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService REQUIREMENTS = ['pystride==0.1.7'] diff --git a/homeassistant/components/notify/synology_chat.py b/homeassistant/components/notify/synology_chat.py index 922631b40453f2..023586ae532983 100644 --- a/homeassistant/components/notify/synology_chat.py +++ b/homeassistant/components/notify/synology_chat.py @@ -4,17 +4,17 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.synology_chat/ """ -import logging import json +import logging import requests import voluptuous as vol -from homeassistant.components.notify import ( - BaseNotificationService, PLATFORM_SCHEMA, ATTR_DATA) from homeassistant.const import CONF_RESOURCE, CONF_VERIFY_SSL import homeassistant.helpers.config_validation as cv +from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService + ATTR_FILE_URL = 'file_url' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/notify/syslog.py b/homeassistant/components/notify/syslog.py index 740148e28e505c..4f7900b8d45c9a 100644 --- a/homeassistant/components/notify/syslog.py +++ b/homeassistant/components/notify/syslog.py @@ -8,7 +8,7 @@ import voluptuous as vol -from homeassistant.components.notify import ( +from . import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/notify/telegram.py index 1dff82fa2cdad6..a6975f60fae299 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/notify/telegram.py @@ -8,11 +8,12 @@ import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_MESSAGE, ATTR_TITLE, ATTR_DATA, ATTR_TARGET, - PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import ATTR_LOCATION +from . import ( + ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, PLATFORM_SCHEMA, + BaseNotificationService) + _LOGGER = logging.getLogger(__name__) DOMAIN = 'telegram_bot' diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/notify/twilio_call.py index 538a4fd9512491..4826ab776121fd 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/notify/twilio_call.py @@ -11,8 +11,8 @@ from homeassistant.components.twilio import DATA_TWILIO import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) + +from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/twilio_sms.py b/homeassistant/components/notify/twilio_sms.py index e106c5ae6aa59a..165e743977d276 100644 --- a/homeassistant/components/notify/twilio_sms.py +++ b/homeassistant/components/notify/twilio_sms.py @@ -10,8 +10,8 @@ from homeassistant.components.twilio import DATA_TWILIO import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) + +from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ["twilio"] diff --git a/homeassistant/components/notify/twitter.py b/homeassistant/components/notify/twitter.py index d494952716e981..43d977d26a63e5 100644 --- a/homeassistant/components/notify/twitter.py +++ b/homeassistant/components/notify/twitter.py @@ -4,21 +4,21 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/notify.twitter/ """ +from datetime import datetime, timedelta +from functools import partial import json import logging import mimetypes import os -from datetime import timedelta, datetime -from functools import partial import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.notify import ( - ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import CONF_ACCESS_TOKEN, CONF_USERNAME +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_point_in_time +from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService + REQUIREMENTS = ['TwitterAPI==2.5.9'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/xmpp.py b/homeassistant/components/notify/xmpp.py index 462dd007d530fb..3827674316b66a 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/notify/xmpp.py @@ -14,13 +14,14 @@ import requests import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) from homeassistant.const import ( CONF_PASSWORD, CONF_RECIPIENT, CONF_RESOURCE, CONF_ROOM, CONF_SENDER) import homeassistant.helpers.config_validation as cv import homeassistant.helpers.template as template_helper +from . import ( + ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) + REQUIREMENTS = ['slixmpp==1.4.2'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/yessssms.py b/homeassistant/components/notify/yessssms.py index e16e384ca25fe1..19efa2a53d8b34 100644 --- a/homeassistant/components/notify/yessssms.py +++ b/homeassistant/components/notify/yessssms.py @@ -8,11 +8,10 @@ import voluptuous as vol -from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_RECIPIENT +from homeassistant.const import CONF_PASSWORD, CONF_RECIPIENT, CONF_USERNAME import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, BaseNotificationService REQUIREMENTS = ['YesssSMS==0.2.3'] diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index f52d2c7b5017d8..27e909f8f04e2c 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -4,29 +4,22 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/climate.nuheat/ """ -import logging from datetime import timedelta +import logging import voluptuous as vol from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - DOMAIN, - SUPPORT_HOLD_MODE, - SUPPORT_OPERATION_MODE, - SUPPORT_TARGET_TEMPERATURE, - STATE_AUTO, - STATE_HEAT, - STATE_IDLE) -from homeassistant.components.nuheat import DOMAIN as NUHEAT_DOMAIN + DOMAIN, STATE_AUTO, STATE_HEAT, STATE_IDLE, SUPPORT_HOLD_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - ATTR_ENTITY_ID, - ATTR_TEMPERATURE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT) + ATTR_ENTITY_ID, ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT) import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle +from . import DOMAIN as NUHEAT_DOMAIN + DEPENDENCIES = ["nuheat"] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/octoprint/binary_sensor.py b/homeassistant/components/octoprint/binary_sensor.py index cb860177796176..be3381f3bc8a2e 100644 --- a/homeassistant/components/octoprint/binary_sensor.py +++ b/homeassistant/components/octoprint/binary_sensor.py @@ -3,10 +3,10 @@ import requests -from homeassistant.components.octoprint import (BINARY_SENSOR_TYPES, - DOMAIN as COMPONENT_DOMAIN) from homeassistant.components.binary_sensor import BinarySensorDevice +from . import BINARY_SENSOR_TYPES, DOMAIN as COMPONENT_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['octoprint'] diff --git a/homeassistant/components/octoprint/sensor.py b/homeassistant/components/octoprint/sensor.py index 2df307f02ef524..f07d88d11da3ff 100644 --- a/homeassistant/components/octoprint/sensor.py +++ b/homeassistant/components/octoprint/sensor.py @@ -3,11 +3,11 @@ import requests -from homeassistant.components.octoprint import (SENSOR_TYPES, - DOMAIN as COMPONENT_DOMAIN) -from homeassistant.const import (TEMP_CELSIUS) +from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import DOMAIN as COMPONENT_DOMAIN, SENSOR_TYPES + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['octoprint'] diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index b35998c807bce2..d0b60a257705a6 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -2,12 +2,12 @@ import logging from homeassistant.components.binary_sensor import ( - BinarySensorDevice, ENTITY_ID_FORMAT) -from homeassistant.components.opentherm_gw import ( - DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE) + ENTITY_ID_FORMAT, BinarySensorDevice) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import async_generate_entity_id +from . import DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE + _LOGGER = logging.getLogger(__name__) DEVICE_CLASS_COLD = 'cold' diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 1a7c031638f79f..58ce49a9b02bfb 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -3,14 +3,15 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_IDLE, STATE_HEAT, STATE_COOL, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.opentherm_gw import ( + STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ( + ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, PRECISION_TENTHS, + PRECISION_WHOLE, TEMP_CELSIUS) +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( CONF_FLOOR_TEMP, CONF_PRECISION, DATA_DEVICE, DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE) -from homeassistant.const import (ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, - PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS) -from homeassistant.helpers.dispatcher import async_dispatcher_connect _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index 070f847e5e53c0..5c64b8ab719b02 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -1,13 +1,13 @@ """Support for OpenTherm Gateway sensors.""" import logging -from homeassistant.components.opentherm_gw import ( - DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE) from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity, async_generate_entity_id +from . import DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE + _LOGGER = logging.getLogger(__name__) UNIT_BAR = 'bar' diff --git a/homeassistant/components/openuv/binary_sensor.py b/homeassistant/components/openuv/binary_sensor.py index b790427b228006..cfc82a7572954d 100644 --- a/homeassistant/components/openuv/binary_sensor.py +++ b/homeassistant/components/openuv/binary_sensor.py @@ -2,13 +2,14 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.openuv import ( - BINARY_SENSORS, DATA_OPENUV_CLIENT, DATA_PROTECTION_WINDOW, DOMAIN, - TOPIC_UPDATE, TYPE_PROTECTION_WINDOW, OpenUvEntity) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util.dt import as_local, parse_datetime, utcnow +from . import ( + BINARY_SENSORS, DATA_OPENUV_CLIENT, DATA_PROTECTION_WINDOW, DOMAIN, + TOPIC_UPDATE, TYPE_PROTECTION_WINDOW, OpenUvEntity) + _LOGGER = logging.getLogger(__name__) ATTR_PROTECTION_WINDOW_ENDING_TIME = 'end_time' ATTR_PROTECTION_WINDOW_ENDING_UV = 'end_uv' diff --git a/homeassistant/components/openuv/sensor.py b/homeassistant/components/openuv/sensor.py index 489a100a5e5281..42780d57b3c252 100644 --- a/homeassistant/components/openuv/sensor.py +++ b/homeassistant/components/openuv/sensor.py @@ -1,15 +1,16 @@ """Support for OpenUV sensors.""" import logging -from homeassistant.components.openuv import ( +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.util.dt import as_local, parse_datetime + +from . import ( DATA_OPENUV_CLIENT, DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, TYPE_CURRENT_OZONE_LEVEL, TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, TYPE_MAX_UV_INDEX, TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, TYPE_SAFE_EXPOSURE_TIME_3, TYPE_SAFE_EXPOSURE_TIME_4, TYPE_SAFE_EXPOSURE_TIME_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.util.dt import as_local, parse_datetime _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/owlet/binary_sensor.py b/homeassistant/components/owlet/binary_sensor.py index cb66278150aea7..bcdd0fec11fca1 100644 --- a/homeassistant/components/owlet/binary_sensor.py +++ b/homeassistant/components/owlet/binary_sensor.py @@ -2,9 +2,9 @@ from datetime import timedelta from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.owlet import DOMAIN as OWLET_DOMAIN from homeassistant.util import dt as dt_util +from . import DOMAIN as OWLET_DOMAIN from .const import SENSOR_BASE_STATION, SENSOR_MOVEMENT SCAN_INTERVAL = timedelta(seconds=120) diff --git a/homeassistant/components/owlet/sensor.py b/homeassistant/components/owlet/sensor.py index b91cc387718646..849e0858ade977 100644 --- a/homeassistant/components/owlet/sensor.py +++ b/homeassistant/components/owlet/sensor.py @@ -1,10 +1,10 @@ """Support for Owlet sensors.""" from datetime import timedelta -from homeassistant.components.owlet import DOMAIN as OWLET_DOMAIN from homeassistant.helpers.entity import Entity from homeassistant.util import dt as dt_util +from . import DOMAIN as OWLET_DOMAIN from .const import SENSOR_HEART_RATE, SENSOR_OXYGEN_LEVEL SCAN_INTERVAL = timedelta(seconds=120) diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index be8698a47b1a7a..f1214b62b0edc0 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -9,12 +9,11 @@ from homeassistant.components import zone as zone_comp from homeassistant.components.device_tracker import ( - ATTR_SOURCE_TYPE, SOURCE_TYPE_BLUETOOTH_LE, SOURCE_TYPE_GPS -) -from homeassistant.components.owntracks import DOMAIN as OT_DOMAIN + ATTR_SOURCE_TYPE, SOURCE_TYPE_BLUETOOTH_LE, SOURCE_TYPE_GPS) from homeassistant.const import STATE_HOME -from homeassistant.util import slugify, decorator +from homeassistant.util import decorator, slugify +from . import DOMAIN as OT_DOMAIN DEPENDENCIES = ['owntracks'] diff --git a/homeassistant/components/plum_lightpad/light.py b/homeassistant/components/plum_lightpad/light.py index 43cceaa671f85a..233539560f4b8a 100644 --- a/homeassistant/components/plum_lightpad/light.py +++ b/homeassistant/components/plum_lightpad/light.py @@ -1,9 +1,10 @@ """Support for Plum Lightpad lights.""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) -from homeassistant.components.plum_lightpad import PLUM_DATA import homeassistant.util.color as color_util +from . import PLUM_DATA + DEPENDENCIES = ['plum_lightpad'] diff --git a/homeassistant/components/point/alarm_control_panel.py b/homeassistant/components/point/alarm_control_panel.py index a50dffe42b9a85..4fd5ffea641ad5 100644 --- a/homeassistant/components/point/alarm_control_panel.py +++ b/homeassistant/components/point/alarm_control_panel.py @@ -3,13 +3,13 @@ from homeassistant.components.alarm_control_panel import ( DOMAIN, AlarmControlPanel) -from homeassistant.components.point.const import ( - DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW, SIGNAL_WEBHOOK) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .const import DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW, SIGNAL_WEBHOOK + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/point/binary_sensor.py b/homeassistant/components/point/binary_sensor.py index c29ce42168219d..2276d4e2fb5d30 100644 --- a/homeassistant/components/point/binary_sensor.py +++ b/homeassistant/components/point/binary_sensor.py @@ -2,12 +2,12 @@ import logging from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice -from homeassistant.components.point import MinutPointEntity -from homeassistant.components.point.const import ( - DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW, SIGNAL_WEBHOOK) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import MinutPointEntity +from .const import DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW, SIGNAL_WEBHOOK + _LOGGER = logging.getLogger(__name__) EVENTS = { diff --git a/homeassistant/components/point/sensor.py b/homeassistant/components/point/sensor.py index 90b83ba42e3ee2..46399c82af4dce 100644 --- a/homeassistant/components/point/sensor.py +++ b/homeassistant/components/point/sensor.py @@ -1,9 +1,6 @@ """Support for Minut Point sensors.""" import logging -from homeassistant.components.point import MinutPointEntity -from homeassistant.components.point.const import ( - DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW) from homeassistant.components.sensor import DOMAIN from homeassistant.const import ( DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, @@ -11,6 +8,9 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util.dt import parse_datetime +from . import MinutPointEntity +from .const import DOMAIN as POINT_DOMAIN, POINT_DISCOVERY_NEW + _LOGGER = logging.getLogger(__name__) DEVICE_CLASS_SOUND = 'sound_level' diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index 087d3f89f809f7..edefb5e47098eb 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -6,8 +6,9 @@ """ import logging -from homeassistant.components.ps4.config_flow import PlayStation4FlowHandler # noqa: pylint: disable=unused-import -from homeassistant.components.ps4.const import DOMAIN # noqa: pylint: disable=unused-import +from .config_flow import ( # noqa pylint: disable=unused-import + PlayStation4FlowHandler) +from .const import DOMAIN # noqa: pylint: disable=unused-import _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ps4/config_flow.py b/homeassistant/components/ps4/config_flow.py index d000ed1f7e7eef..482c7383d89d76 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -5,11 +5,11 @@ import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.ps4.const import ( - DEFAULT_NAME, DEFAULT_REGION, DOMAIN, REGIONS) from homeassistant.const import ( CONF_CODE, CONF_HOST, CONF_IP_ADDRESS, CONF_NAME, CONF_REGION, CONF_TOKEN) +from .const import DEFAULT_NAME, DEFAULT_REGION, DOMAIN, REGIONS + _LOGGER = logging.getLogger(__name__) UDP_PORT = 987 diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index e2b8e97f425c7a..60b656a469dcbc 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -9,20 +9,18 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.media_player import ( - MediaPlayerDevice, ENTITY_IMAGE_URL) + ENTITY_IMAGE_URL, MediaPlayerDevice) from homeassistant.components.media_player.const import ( - MEDIA_TYPE_MUSIC, SUPPORT_SELECT_SOURCE, - SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, -) -from homeassistant.components.ps4.const import DOMAIN as PS4_DOMAIN + MEDIA_TYPE_MUSIC, SUPPORT_SELECT_SOURCE, SUPPORT_STOP, SUPPORT_TURN_OFF, + SUPPORT_TURN_ON) from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_COMMAND, CONF_HOST, CONF_NAME, CONF_REGION, - CONF_TOKEN, STATE_IDLE, STATE_OFF, STATE_PLAYING, -) + ATTR_COMMAND, ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_REGION, + CONF_TOKEN, STATE_IDLE, STATE_OFF, STATE_PLAYING) +import homeassistant.helpers.config_validation as cv from homeassistant.util.json import load_json, save_json +from .const import DOMAIN as PS4_DOMAIN DEPENDENCIES = ['ps4'] diff --git a/homeassistant/components/qwikswitch/binary_sensor.py b/homeassistant/components/qwikswitch/binary_sensor.py index 2fe14773d3a03f..17021f7a9e9bc3 100644 --- a/homeassistant/components/qwikswitch/binary_sensor.py +++ b/homeassistant/components/qwikswitch/binary_sensor.py @@ -7,9 +7,10 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.qwikswitch import QSEntity, DOMAIN as QWIKSWITCH from homeassistant.core import callback +from . import DOMAIN as QWIKSWITCH, QSEntity + DEPENDENCIES = [QWIKSWITCH] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/qwikswitch/light.py b/homeassistant/components/qwikswitch/light.py index 413358d9cee074..46a0a88483b2d5 100644 --- a/homeassistant/components/qwikswitch/light.py +++ b/homeassistant/components/qwikswitch/light.py @@ -4,10 +4,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.qwikswitch/ """ -from homeassistant.components.qwikswitch import ( - QSToggleEntity, DOMAIN as QWIKSWITCH) from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light +from . import DOMAIN as QWIKSWITCH, QSToggleEntity + DEPENDENCIES = [QWIKSWITCH] diff --git a/homeassistant/components/qwikswitch/sensor.py b/homeassistant/components/qwikswitch/sensor.py index c8d33de4baff0b..07d0247e4f60b5 100644 --- a/homeassistant/components/qwikswitch/sensor.py +++ b/homeassistant/components/qwikswitch/sensor.py @@ -6,9 +6,10 @@ """ import logging -from homeassistant.components.qwikswitch import DOMAIN as QWIKSWITCH, QSEntity from homeassistant.core import callback +from . import DOMAIN as QWIKSWITCH, QSEntity + DEPENDENCIES = [QWIKSWITCH] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/qwikswitch/switch.py b/homeassistant/components/qwikswitch/switch.py index 3b8e6d74df2554..ec544df8c7531d 100644 --- a/homeassistant/components/qwikswitch/switch.py +++ b/homeassistant/components/qwikswitch/switch.py @@ -4,10 +4,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.qwikswitch/ """ -from homeassistant.components.qwikswitch import ( - QSToggleEntity, DOMAIN as QWIKSWITCH) from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as QWIKSWITCH, QSToggleEntity + DEPENDENCIES = [QWIKSWITCH] diff --git a/homeassistant/components/rachio/binary_sensor.py b/homeassistant/components/rachio/binary_sensor.py index 36a32c79c5cdeb..9cf57ea323064f 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -8,17 +8,13 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.rachio import (DOMAIN as DOMAIN_RACHIO, - KEY_DEVICE_ID, - KEY_STATUS, - KEY_SUBTYPE, - SIGNAL_RACHIO_CONTROLLER_UPDATE, - STATUS_OFFLINE, - STATUS_ONLINE, - SUBTYPE_OFFLINE, - SUBTYPE_ONLINE,) from homeassistant.helpers.dispatcher import dispatcher_connect +from . import ( + DOMAIN as DOMAIN_RACHIO, KEY_DEVICE_ID, KEY_STATUS, KEY_SUBTYPE, + SIGNAL_RACHIO_CONTROLLER_UPDATE, STATUS_OFFLINE, STATUS_ONLINE, + SUBTYPE_OFFLINE, SUBTYPE_ONLINE) + DEPENDENCIES = ['rachio'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 4797aae9a8cd9c..fe584441afd0fa 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -9,26 +9,15 @@ import logging from homeassistant.components.switch import SwitchDevice -from homeassistant.components.rachio import (CONF_MANUAL_RUN_MINS, - DOMAIN as DOMAIN_RACHIO, - KEY_DEVICE_ID, - KEY_ENABLED, - KEY_ID, - KEY_NAME, - KEY_ON, - KEY_SUBTYPE, - KEY_SUMMARY, - KEY_ZONE_ID, - KEY_ZONE_NUMBER, - SIGNAL_RACHIO_CONTROLLER_UPDATE, - SIGNAL_RACHIO_ZONE_UPDATE, - SUBTYPE_ZONE_STARTED, - SUBTYPE_ZONE_STOPPED, - SUBTYPE_ZONE_COMPLETED, - SUBTYPE_SLEEP_MODE_ON, - SUBTYPE_SLEEP_MODE_OFF) from homeassistant.helpers.dispatcher import dispatcher_connect +from . import ( + CONF_MANUAL_RUN_MINS, DOMAIN as DOMAIN_RACHIO, KEY_DEVICE_ID, KEY_ENABLED, + KEY_ID, KEY_NAME, KEY_ON, KEY_SUBTYPE, KEY_SUMMARY, KEY_ZONE_ID, + KEY_ZONE_NUMBER, SIGNAL_RACHIO_CONTROLLER_UPDATE, + SIGNAL_RACHIO_ZONE_UPDATE, SUBTYPE_SLEEP_MODE_OFF, SUBTYPE_SLEEP_MODE_ON, + SUBTYPE_ZONE_COMPLETED, SUBTYPE_ZONE_STARTED, SUBTYPE_ZONE_STOPPED) + DEPENDENCIES = ['rachio'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainbird/sensor.py b/homeassistant/components/rainbird/sensor.py index 1af2b771014c21..3d0de04e53ef99 100644 --- a/homeassistant/components/rainbird/sensor.py +++ b/homeassistant/components/rainbird/sensor.py @@ -8,12 +8,13 @@ import voluptuous as vol -from homeassistant.components.rainbird import DATA_RAINBIRD -import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import DATA_RAINBIRD + DEPENDENCIES = ['rainbird'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainbird/switch.py b/homeassistant/components/rainbird/switch.py index 86f25102f707e3..2031769b343244 100644 --- a/homeassistant/components/rainbird/switch.py +++ b/homeassistant/components/rainbird/switch.py @@ -9,13 +9,14 @@ import voluptuous as vol -from homeassistant.components.rainbird import DATA_RAINBIRD -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.const import (CONF_SWITCHES, CONF_ZONE, - CONF_FRIENDLY_NAME, CONF_TRIGGER_TIME, - CONF_SCAN_INTERVAL) +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import ( + CONF_FRIENDLY_NAME, CONF_SCAN_INTERVAL, CONF_SWITCHES, CONF_TRIGGER_TIME, + CONF_ZONE) from homeassistant.helpers import config_validation as cv +from . import DATA_RAINBIRD + DEPENDENCIES = ['rainbird'] DOMAIN = 'rainbird' diff --git a/homeassistant/components/raincloud/binary_sensor.py b/homeassistant/components/raincloud/binary_sensor.py index 810c7d201cbfb0..cb66fc3c6af8ec 100644 --- a/homeassistant/components/raincloud/binary_sensor.py +++ b/homeassistant/components/raincloud/binary_sensor.py @@ -8,12 +8,12 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.raincloud import ( - BINARY_SENSORS, DATA_RAINCLOUD, ICON_MAP, RainCloudEntity) from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv + +from . import BINARY_SENSORS, DATA_RAINCLOUD, ICON_MAP, RainCloudEntity DEPENDENCIES = ['raincloud'] diff --git a/homeassistant/components/raincloud/sensor.py b/homeassistant/components/raincloud/sensor.py index 15a346880f3367..8bcccf06171a28 100644 --- a/homeassistant/components/raincloud/sensor.py +++ b/homeassistant/components/raincloud/sensor.py @@ -8,13 +8,13 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.raincloud import ( - DATA_RAINCLOUD, ICON_MAP, RainCloudEntity, SENSORS) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.icon import icon_for_battery_level +from . import DATA_RAINCLOUD, ICON_MAP, SENSORS, RainCloudEntity + DEPENDENCIES = ['raincloud'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/raincloud/switch.py b/homeassistant/components/raincloud/switch.py index 1b76e8974b09d4..3901e1e0bd89ce 100644 --- a/homeassistant/components/raincloud/switch.py +++ b/homeassistant/components/raincloud/switch.py @@ -3,13 +3,13 @@ import voluptuous as vol +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS import homeassistant.helpers.config_validation as cv -from homeassistant.components.raincloud import ( - ALLOWED_WATERING_TIME, ATTRIBUTION, CONF_WATERING_TIME, - DATA_RAINCLOUD, DEFAULT_WATERING_TIME, RainCloudEntity, SWITCHES) -from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA -from homeassistant.const import ( - CONF_MONITORED_CONDITIONS, ATTR_ATTRIBUTION) + +from . import ( + ALLOWED_WATERING_TIME, ATTRIBUTION, CONF_WATERING_TIME, DATA_RAINCLOUD, + DEFAULT_WATERING_TIME, SWITCHES, RainCloudEntity) DEPENDENCIES = ['raincloud'] diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index efae93303650ad..929dbcf314c403 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -7,13 +7,14 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.rainmachine import ( +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( BINARY_SENSORS, DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, SENSOR_UPDATE_TOPIC, TYPE_FREEZE, TYPE_FREEZE_PROTECTION, TYPE_HOT_DAYS, TYPE_HOURLY, TYPE_MONTH, TYPE_RAINDELAY, TYPE_RAINSENSOR, TYPE_WEEKDAY, RainMachineEntity) -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect DEPENDENCIES = ['rainmachine'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainmachine/const.py b/homeassistant/components/rainmachine/const.py index e0e79e8c160ed4..4d08a871f61c17 100644 --- a/homeassistant/components/rainmachine/const.py +++ b/homeassistant/components/rainmachine/const.py @@ -1,8 +1,8 @@ """Define constants for the SimpliSafe component.""" -import logging from datetime import timedelta +import logging -LOGGER = logging.getLogger('homeassistant.components.rainmachine') +LOGGER = logging.getLogger('.') DOMAIN = 'rainmachine' diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 86a97bc291cfe7..908daa2c83dc0a 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -6,12 +6,13 @@ """ import logging -from homeassistant.components.rainmachine import ( - DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, SENSOR_UPDATE_TOPIC, SENSORS, - RainMachineEntity) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import ( + DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, SENSOR_UPDATE_TOPIC, SENSORS, + RainMachineEntity) + DEPENDENCIES = ['rainmachine'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index e3a1ddab912b61..6b658c0fcbf173 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -4,18 +4,19 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/switch.rainmachine/ """ -import logging from datetime import datetime +import logging -from homeassistant.components.rainmachine import ( - DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, PROGRAM_UPDATE_TOPIC, - ZONE_UPDATE_TOPIC, RainMachineEntity) -from homeassistant.const import ATTR_ID from homeassistant.components.switch import SwitchDevice +from homeassistant.const import ATTR_ID from homeassistant.core import callback from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send) +from . import ( + DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, PROGRAM_UPDATE_TOPIC, + ZONE_UPDATE_TOPIC, RainMachineEntity) + DEPENDENCIES = ['rainmachine'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/raspihats/binary_sensor.py b/homeassistant/components/raspihats/binary_sensor.py index b0ebc2e3579acb..29fa474f781270 100644 --- a/homeassistant/components/raspihats/binary_sensor.py +++ b/homeassistant/components/raspihats/binary_sensor.py @@ -5,13 +5,14 @@ from homeassistant.components.binary_sensor import ( PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.raspihats import ( - CONF_BOARD, CONF_CHANNELS, CONF_I2C_HATS, CONF_INDEX, CONF_INVERT_LOGIC, - I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException) from homeassistant.const import ( CONF_ADDRESS, CONF_DEVICE_CLASS, CONF_NAME, DEVICE_DEFAULT_NAME) import homeassistant.helpers.config_validation as cv +from . import ( + CONF_BOARD, CONF_CHANNELS, CONF_I2C_HATS, CONF_INDEX, CONF_INVERT_LOGIC, + I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['raspihats'] diff --git a/homeassistant/components/raspihats/switch.py b/homeassistant/components/raspihats/switch.py index 26fcda3c8d7c69..93538682ad8341 100644 --- a/homeassistant/components/raspihats/switch.py +++ b/homeassistant/components/raspihats/switch.py @@ -3,14 +3,15 @@ import voluptuous as vol -from homeassistant.components.raspihats import ( - CONF_BOARD, CONF_CHANNELS, CONF_I2C_HATS, CONF_INDEX, CONF_INITIAL_STATE, - CONF_INVERT_LOGIC, I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException) from homeassistant.components.switch import PLATFORM_SCHEMA from homeassistant.const import CONF_ADDRESS, CONF_NAME, DEVICE_DEFAULT_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import ToggleEntity +from . import ( + CONF_BOARD, CONF_CHANNELS, CONF_I2C_HATS, CONF_INDEX, CONF_INITIAL_STATE, + CONF_INVERT_LOGIC, I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['raspihats'] diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index 1599d5bce3eb79..1a94159290fd53 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -6,19 +6,20 @@ """ import logging -import voluptuous as vol from requests.auth import HTTPBasicAuth, HTTPDigestAuth +import voluptuous as vol from homeassistant.components.binary_sensor import ( - BinarySensorDevice, DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA) -from homeassistant.components.rest.sensor import RestData + DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import ( - CONF_PAYLOAD, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_METHOD, CONF_RESOURCE, - CONF_VERIFY_SSL, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT, - CONF_HEADERS, CONF_AUTHENTICATION, HTTP_BASIC_AUTHENTICATION, - HTTP_DIGEST_AUTHENTICATION, CONF_DEVICE_CLASS) -import homeassistant.helpers.config_validation as cv + CONF_AUTHENTICATION, CONF_DEVICE_CLASS, CONF_HEADERS, CONF_METHOD, + CONF_NAME, CONF_PASSWORD, CONF_PAYLOAD, CONF_RESOURCE, CONF_TIMEOUT, + CONF_USERNAME, CONF_VALUE_TEMPLATE, CONF_VERIFY_SSL, + HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION) from homeassistant.exceptions import PlatformNotReady +import homeassistant.helpers.config_validation as cv + +from .sensor import RestData _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rflink/binary_sensor.py b/homeassistant/components/rflink/binary_sensor.py index 73b912d62dae94..5318642a5b1688 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -10,13 +10,12 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.rflink import ( - CONF_ALIASES, CONF_DEVICES, RflinkDevice) -from homeassistant.const import ( - CONF_FORCE_UPDATE, CONF_NAME, CONF_DEVICE_CLASS) +from homeassistant.const import CONF_DEVICE_CLASS, CONF_FORCE_UPDATE, CONF_NAME import homeassistant.helpers.config_validation as cv import homeassistant.helpers.event as evt +from . import CONF_ALIASES, CONF_DEVICES, RflinkDevice + CONF_OFF_DELAY = 'off_delay' DEFAULT_FORCE_UPDATE = False diff --git a/homeassistant/components/rflink/cover.py b/homeassistant/components/rflink/cover.py index cdc7cac3adb052..f91ef1cc6828bf 100644 --- a/homeassistant/components/rflink/cover.py +++ b/homeassistant/components/rflink/cover.py @@ -8,15 +8,15 @@ import voluptuous as vol -from homeassistant.components.rflink import ( +from homeassistant.components.cover import PLATFORM_SCHEMA, CoverDevice +from homeassistant.const import CONF_NAME, STATE_OPEN +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.restore_state import RestoreEntity + +from . import ( CONF_ALIASES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, CONF_GROUP, CONF_GROUP_ALIASES, CONF_NOGROUP_ALIASES, CONF_SIGNAL_REPETITIONS, DEVICE_DEFAULTS_SCHEMA, RflinkCommand) -from homeassistant.components.cover import ( - CoverDevice, PLATFORM_SCHEMA) -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.restore_state import RestoreEntity -from homeassistant.const import CONF_NAME, STATE_OPEN DEPENDENCIES = ['rflink'] diff --git a/homeassistant/components/rflink/light.py b/homeassistant/components/rflink/light.py index 726433b4f7030e..cdb34328b514a8 100644 --- a/homeassistant/components/rflink/light.py +++ b/homeassistant/components/rflink/light.py @@ -10,15 +10,15 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.rflink import ( +from homeassistant.const import CONF_NAME, CONF_TYPE +import homeassistant.helpers.config_validation as cv + +from . import ( CONF_ALIASES, CONF_ALIASSES, CONF_AUTOMATIC_ADD, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, CONF_GROUP, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, DATA_DEVICE_REGISTER, DEVICE_DEFAULTS_SCHEMA, - EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, - remove_deprecated) -import homeassistant.helpers.config_validation as cv -from homeassistant.const import (CONF_NAME, CONF_TYPE) + EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, remove_deprecated) DEPENDENCIES = ['rflink'] diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index f3ec776fda8102..e46cc09d0ba457 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -8,17 +8,17 @@ import voluptuous as vol -from homeassistant.components.rflink import ( - CONF_ALIASES, CONF_ALIASSES, CONF_AUTOMATIC_ADD, CONF_DEVICES, - DATA_DEVICE_REGISTER, DATA_ENTITY_LOOKUP, EVENT_KEY_ID, - EVENT_KEY_SENSOR, EVENT_KEY_UNIT, RflinkDevice, remove_deprecated, - SIGNAL_AVAILABILITY, SIGNAL_HANDLE_EVENT, TMP_ENTITY) -from homeassistant.components.sensor import ( - PLATFORM_SCHEMA) -import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_UNIT_OF_MEASUREMENT) -from homeassistant.helpers.dispatcher import (async_dispatcher_connect) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( + CONF_ALIASES, CONF_ALIASSES, CONF_AUTOMATIC_ADD, CONF_DEVICES, + DATA_DEVICE_REGISTER, DATA_ENTITY_LOOKUP, EVENT_KEY_ID, EVENT_KEY_SENSOR, + EVENT_KEY_UNIT, SIGNAL_AVAILABILITY, SIGNAL_HANDLE_EVENT, TMP_ENTITY, + RflinkDevice, remove_deprecated) DEPENDENCIES = ['rflink'] diff --git a/homeassistant/components/rflink/switch.py b/homeassistant/components/rflink/switch.py index 25e4e367fdf987..a1470bf115fe8d 100644 --- a/homeassistant/components/rflink/switch.py +++ b/homeassistant/components/rflink/switch.py @@ -8,15 +8,15 @@ import voluptuous as vol -from homeassistant.components.rflink import ( +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import CONF_NAME +import homeassistant.helpers.config_validation as cv + +from . import ( CONF_ALIASES, CONF_ALIASSES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, CONF_GROUP, CONF_GROUP_ALIASES, CONF_GROUP_ALIASSES, CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, DEVICE_DEFAULTS_SCHEMA, SwitchableRflinkDevice, remove_deprecated) -from homeassistant.components.switch import ( - PLATFORM_SCHEMA, SwitchDevice) -import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_NAME DEPENDENCIES = ['rflink'] diff --git a/homeassistant/components/rfxtrx/binary_sensor.py b/homeassistant/components/rfxtrx/binary_sensor.py index 9a49bd02b97fc1..d548897fb80ce1 100644 --- a/homeassistant/components/rfxtrx/binary_sensor.py +++ b/homeassistant/components/rfxtrx/binary_sensor.py @@ -6,15 +6,14 @@ from homeassistant.components import rfxtrx from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.rfxtrx import ( - ATTR_NAME, CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_DEVICES, - CONF_FIRE_EVENT, CONF_OFF_DELAY) from homeassistant.const import ( CONF_COMMAND_OFF, CONF_COMMAND_ON, CONF_DEVICE_CLASS, CONF_NAME) -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers import event as evt -from homeassistant.util import dt as dt_util -from homeassistant.util import slugify +from homeassistant.helpers import config_validation as cv, event as evt +from homeassistant.util import dt as dt_util, slugify + +from . import ( + ATTR_NAME, CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_DEVICES, + CONF_FIRE_EVENT, CONF_OFF_DELAY) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rfxtrx/cover.py b/homeassistant/components/rfxtrx/cover.py index 5a657923683b3b..7ac0e2aa43f912 100644 --- a/homeassistant/components/rfxtrx/cover.py +++ b/homeassistant/components/rfxtrx/cover.py @@ -2,13 +2,14 @@ import voluptuous as vol from homeassistant.components import rfxtrx -from homeassistant.components.cover import CoverDevice, PLATFORM_SCHEMA +from homeassistant.components.cover import PLATFORM_SCHEMA, CoverDevice from homeassistant.const import CONF_NAME -from homeassistant.components.rfxtrx import ( - CONF_AUTOMATIC_ADD, CONF_FIRE_EVENT, DEFAULT_SIGNAL_REPETITIONS, - CONF_SIGNAL_REPETITIONS, CONF_DEVICES) from homeassistant.helpers import config_validation as cv +from . import ( + CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, + DEFAULT_SIGNAL_REPETITIONS) + DEPENDENCIES = ['rfxtrx'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rfxtrx/light.py b/homeassistant/components/rfxtrx/light.py index d0b75c2f9627e1..3320a67214e2bd 100644 --- a/homeassistant/components/rfxtrx/light.py +++ b/homeassistant/components/rfxtrx/light.py @@ -5,13 +5,14 @@ from homeassistant.components import rfxtrx from homeassistant.components.light import ( - ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light, PLATFORM_SCHEMA) + ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light) from homeassistant.const import CONF_NAME -from homeassistant.components.rfxtrx import ( - CONF_AUTOMATIC_ADD, CONF_FIRE_EVENT, DEFAULT_SIGNAL_REPETITIONS, - CONF_SIGNAL_REPETITIONS, CONF_DEVICES) from homeassistant.helpers import config_validation as cv +from . import ( + CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, + DEFAULT_SIGNAL_REPETITIONS) + DEPENDENCIES = ['rfxtrx'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rfxtrx/sensor.py b/homeassistant/components/rfxtrx/sensor.py index 74c64635563349..cc54320cb672eb 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -4,15 +4,16 @@ import voluptuous as vol from homeassistant.components import rfxtrx -from homeassistant.components.rfxtrx import ( - ATTR_DATA_TYPE, ATTR_FIRE_EVENT, CONF_AUTOMATIC_ADD, CONF_DATA_TYPE, - CONF_DEVICES, CONF_FIRE_EVENT, DATA_TYPES) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME, CONF_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import slugify +from . import ( + ATTR_DATA_TYPE, ATTR_FIRE_EVENT, CONF_AUTOMATIC_ADD, CONF_DATA_TYPE, + CONF_DEVICES, CONF_FIRE_EVENT, DATA_TYPES) + DEPENDENCIES = ['rfxtrx'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rfxtrx/switch.py b/homeassistant/components/rfxtrx/switch.py index 141cf2c2c1a617..908c07ea745795 100644 --- a/homeassistant/components/rfxtrx/switch.py +++ b/homeassistant/components/rfxtrx/switch.py @@ -4,12 +4,13 @@ import voluptuous as vol from homeassistant.components import rfxtrx -from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA -from homeassistant.components.rfxtrx import ( - CONF_AUTOMATIC_ADD, CONF_FIRE_EVENT, DEFAULT_SIGNAL_REPETITIONS, - CONF_SIGNAL_REPETITIONS, CONF_DEVICES) -from homeassistant.helpers import config_validation as cv +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import CONF_NAME +from homeassistant.helpers import config_validation as cv + +from . import ( + CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, + DEFAULT_SIGNAL_REPETITIONS) DEPENDENCIES = ['rfxtrx'] diff --git a/homeassistant/components/ring/binary_sensor.py b/homeassistant/components/ring/binary_sensor.py index 79fc61a62d47bd..bcc365a2e83a09 100644 --- a/homeassistant/components/ring/binary_sensor.py +++ b/homeassistant/components/ring/binary_sensor.py @@ -4,20 +4,18 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.ring/ """ -import logging from datetime import timedelta +import logging import voluptuous as vol -import homeassistant.helpers.config_validation as cv - -from homeassistant.components.ring import ( - ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DATA_RING) +from homeassistant.components.binary_sensor import ( + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) +import homeassistant.helpers.config_validation as cv -from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) +from . import ATTRIBUTION, DATA_RING, DEFAULT_ENTITY_NAMESPACE DEPENDENCIES = ['ring'] diff --git a/homeassistant/components/ring/camera.py b/homeassistant/components/ring/camera.py index ce9ceb7b76fc27..efcdf8599a9bb8 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -5,21 +5,20 @@ https://home-assistant.io/components/camera.ring/ """ import asyncio -import logging - from datetime import timedelta +import logging import voluptuous as vol -from homeassistant.helpers import config_validation as cv -from homeassistant.components.ring import ( - DATA_RING, ATTRIBUTION, NOTIFICATION_ID) -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ATTR_ATTRIBUTION, CONF_SCAN_INTERVAL +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream from homeassistant.util import dt as dt_util +from . import ATTRIBUTION, DATA_RING, NOTIFICATION_ID + CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' DEPENDENCIES = ['ring', 'ffmpeg'] diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index d58e0cf8b3f5dc..5e323d89ad895d 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -9,16 +9,15 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.ring import ( - ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DATA_RING) from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS, - ATTR_ATTRIBUTION) + ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level +from . import ATTRIBUTION, DATA_RING, DEFAULT_ENTITY_NAMESPACE + DEPENDENCIES = ['ring'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index ca8fc64eea4a5c..4968725a4befc7 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -1,11 +1,11 @@ """Support for monitoring an SABnzbd NZB client.""" import logging -from homeassistant.components.sabnzbd import DATA_SABNZBD, \ - SIGNAL_SABNZBD_UPDATED, SENSOR_TYPES from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +from . import DATA_SABNZBD, SENSOR_TYPES, SIGNAL_SABNZBD_UPDATED + DEPENDENCIES = ['sabnzbd'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 35eedabd58a595..86392c0902d897 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -5,8 +5,7 @@ import voluptuous as vol -from homeassistant.const import ( - ATTR_ENTITY_ID, CONF_PLATFORM, SERVICE_TURN_ON) +from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM, SERVICE_TURN_ON import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent @@ -28,9 +27,8 @@ def _hass_domain_validator(config): def _platform_validator(config): """Validate it is a valid platform.""" try: - platform = importlib.import_module( - 'homeassistant.components.scene.{}'.format( - config[CONF_PLATFORM])) + platform = importlib.import_module('.{}'.format(config[CONF_PLATFORM]), + __name__) except ImportError: try: platform = importlib.import_module( diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index 96e24138b4a9d4..86af6c34694a00 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -3,13 +3,14 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.components.scene import Scene, STATES from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_STATE, CONF_ENTITIES, CONF_NAME, CONF_PLATFORM, STATE_OFF, STATE_ON) from homeassistant.core import State -from homeassistant.helpers.state import async_reproduce_state, HASS_DOMAIN +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.state import HASS_DOMAIN, async_reproduce_state + +from . import STATES, Scene PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): HASS_DOMAIN, diff --git a/homeassistant/components/sense/binary_sensor.py b/homeassistant/components/sense/binary_sensor.py index 0341f65e963fe0..da9bae3cc84788 100644 --- a/homeassistant/components/sense/binary_sensor.py +++ b/homeassistant/components/sense/binary_sensor.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.sense import SENSE_DATA + +from . import SENSE_DATA DEPENDENCIES = ['sense'] diff --git a/homeassistant/components/sense/sensor.py b/homeassistant/components/sense/sensor.py index ffde584c0ae9d5..0224884e18a5cd 100644 --- a/homeassistant/components/sense/sensor.py +++ b/homeassistant/components/sense/sensor.py @@ -2,11 +2,12 @@ from datetime import timedelta import logging -from homeassistant.components.sense import SENSE_DATA -from homeassistant.const import POWER_WATT, ENERGY_KILO_WATT_HOUR +from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle +from . import SENSE_DATA + _LOGGER = logging.getLogger(__name__) ACTIVE_NAME = 'Energy' diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index 9fdeea73da8d8b..6931177869853c 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -3,14 +3,14 @@ import re import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.simplisafe.const import ( - DATA_CLIENT, DOMAIN, TOPIC_UPDATE) from homeassistant.const import ( CONF_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .const import DATA_CLIENT, DOMAIN, TOPIC_UPDATE + _LOGGER = logging.getLogger(__name__) ATTR_ALARM_ACTIVE = 'alarm_active' diff --git a/homeassistant/components/sisyphus/light.py b/homeassistant/components/sisyphus/light.py index c9d209596968cc..182e5e78198dc1 100644 --- a/homeassistant/components/sisyphus/light.py +++ b/homeassistant/components/sisyphus/light.py @@ -1,9 +1,10 @@ """Support for the light on the Sisyphus Kinetic Art Table.""" import logging -from homeassistant.const import CONF_NAME from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light -from homeassistant.components.sisyphus import DATA_SISYPHUS +from homeassistant.const import CONF_NAME + +from . import DATA_SISYPHUS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sisyphus/media_player.py b/homeassistant/components/sisyphus/media_player.py index 463ac2b6cd1c20..11546c3fd43822 100644 --- a/homeassistant/components/sisyphus/media_player.py +++ b/homeassistant/components/sisyphus/media_player.py @@ -6,10 +6,11 @@ SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_SHUFFLE_SET, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) -from homeassistant.components.sisyphus import DATA_SISYPHUS from homeassistant.const import ( CONF_HOST, CONF_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) +from . import DATA_SISYPHUS + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['sisyphus'] diff --git a/homeassistant/components/skybell/binary_sensor.py b/homeassistant/components/skybell/binary_sensor.py index 169e1b51a4e28e..8c2b83552588c5 100644 --- a/homeassistant/components/skybell/binary_sensor.py +++ b/homeassistant/components/skybell/binary_sensor.py @@ -5,13 +5,13 @@ import voluptuous as vol from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.skybell import ( - DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice) + PLATFORM_SCHEMA, BinarySensorDevice) from homeassistant.const import ( CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) import homeassistant.helpers.config_validation as cv +from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice + DEPENDENCIES = ['skybell'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/skybell/camera.py b/homeassistant/components/skybell/camera.py index c22489aa65401c..04b03f84bf751d 100644 --- a/homeassistant/components/skybell/camera.py +++ b/homeassistant/components/skybell/camera.py @@ -5,13 +5,11 @@ import requests import voluptuous as vol -from homeassistant.components.camera import PLATFORM_SCHEMA +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.const import CONF_MONITORED_CONDITIONS import homeassistant.helpers.config_validation as cv -from homeassistant.components.camera import Camera -from homeassistant.components.skybell import ( - DOMAIN as SKYBELL_DOMAIN, SkybellDevice) +from . import DOMAIN as SKYBELL_DOMAIN, SkybellDevice DEPENDENCIES = ['skybell'] diff --git a/homeassistant/components/skybell/light.py b/homeassistant/components/skybell/light.py index 02be279f609f4f..d413f9df4127dc 100644 --- a/homeassistant/components/skybell/light.py +++ b/homeassistant/components/skybell/light.py @@ -1,14 +1,12 @@ """Light/LED support for the Skybell HD Doorbell.""" import logging - from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_HS_COLOR, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) -from homeassistant.components.skybell import ( - DOMAIN as SKYBELL_DOMAIN, SkybellDevice) + ATTR_BRIGHTNESS, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) import homeassistant.util.color as color_util +from . import DOMAIN as SKYBELL_DOMAIN, SkybellDevice + DEPENDENCIES = ['skybell'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/skybell/sensor.py b/homeassistant/components/skybell/sensor.py index 89841ae74ef62d..067e850dfcf1dc 100644 --- a/homeassistant/components/skybell/sensor.py +++ b/homeassistant/components/skybell/sensor.py @@ -5,12 +5,12 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.skybell import ( - DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice) from homeassistant.const import ( CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) import homeassistant.helpers.config_validation as cv +from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice + DEPENDENCIES = ['skybell'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/skybell/switch.py b/homeassistant/components/skybell/switch.py index 32f1d7f9392692..674bbf22a08600 100644 --- a/homeassistant/components/skybell/switch.py +++ b/homeassistant/components/skybell/switch.py @@ -3,14 +3,13 @@ import voluptuous as vol - -from homeassistant.components.skybell import ( - DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice) from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import ( CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS) import homeassistant.helpers.config_validation as cv +from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice + DEPENDENCIES = ['skybell'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/smappee/sensor.py b/homeassistant/components/smappee/sensor.py index 09584851c6a85b..98527c769d9036 100644 --- a/homeassistant/components/smappee/sensor.py +++ b/homeassistant/components/smappee/sensor.py @@ -1,10 +1,11 @@ """Support for monitoring a Smappee energy sensor.""" -import logging from datetime import timedelta +import logging -from homeassistant.components.smappee import DATA_SMAPPEE +from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT from homeassistant.helpers.entity import Entity -from homeassistant.const import POWER_WATT, ENERGY_KILO_WATT_HOUR + +from . import DATA_SMAPPEE DEPENDENCIES = ['smappee'] diff --git a/homeassistant/components/smappee/switch.py b/homeassistant/components/smappee/switch.py index 3b9bee081f7b6c..963caf457fe8db 100644 --- a/homeassistant/components/smappee/switch.py +++ b/homeassistant/components/smappee/switch.py @@ -1,8 +1,9 @@ """Support for interacting with Smappee Comport Plugs.""" import logging -from homeassistant.components.smappee import DATA_SMAPPEE -from homeassistant.components.switch import (SwitchDevice) +from homeassistant.components.switch import SwitchDevice + +from . import DATA_SMAPPEE DEPENDENCIES = ['smappee'] diff --git a/homeassistant/components/smhi/const.py b/homeassistant/components/smhi/const.py index 9689857e546f0f..4cd1d6f7b4be6f 100644 --- a/homeassistant/components/smhi/const.py +++ b/homeassistant/components/smhi/const.py @@ -13,4 +13,4 @@ ENTITY_ID_SENSOR_FORMAT_HOME = ENTITY_ID_SENSOR_FORMAT.format( HOME_LOCATION_NAME) -LOGGER = logging.getLogger('homeassistant.components.smhi') +LOGGER = logging.getLogger('.') diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index 6136d093a33516..fc3399f755ccc5 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -7,8 +7,6 @@ import aiohttp import async_timeout -from homeassistant.components.smhi.const import ( - ATTR_SMHI_CLOUDINESS, ENTITY_ID_SENSOR_FORMAT) from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_PRECIPITATION, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, WeatherEntity) @@ -19,6 +17,8 @@ from homeassistant.helpers import aiohttp_client from homeassistant.util import Throttle, slugify +from .const import ATTR_SMHI_CLOUDINESS, ENTITY_ID_SENSOR_FORMAT + DEPENDENCIES = ['smhi'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 684e25ba59984d..ba7854e4f0d28f 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -1,9 +1,9 @@ """Support to interface with Sonos players.""" +import asyncio import datetime import functools as ft import logging import socket -import asyncio import urllib import async_timeout @@ -11,20 +11,20 @@ import voluptuous as vol from homeassistant.components.media_player import ( - MediaPlayerDevice, PLATFORM_SCHEMA) + PLATFORM_SCHEMA, MediaPlayerDevice) from homeassistant.components.media_player.const import ( - ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, - SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, - SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, - SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_STOP, - SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) -from homeassistant.components.sonos import DOMAIN as SONOS_DOMAIN + ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_CLEAR_PLAYLIST, + SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, + SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_SELECT_SOURCE, + SUPPORT_SHUFFLE_SET, SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_TIME, CONF_HOSTS, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) import homeassistant.helpers.config_validation as cv from homeassistant.util.dt import utcnow +from . import DOMAIN as SONOS_DOMAIN + DEPENDENCIES = ('sonos',) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/spc/alarm_control_panel.py b/homeassistant/components/spc/alarm_control_panel.py index 7adbb6167746fb..623a4b0dbd1d88 100644 --- a/homeassistant/components/spc/alarm_control_panel.py +++ b/homeassistant/components/spc/alarm_control_panel.py @@ -7,12 +7,13 @@ import logging import homeassistant.components.alarm_control_panel as alarm -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.core import callback -from homeassistant.components.spc import (DATA_API, SIGNAL_UPDATE_ALARM) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import DATA_API, SIGNAL_UPDATE_ALARM _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/spc/binary_sensor.py b/homeassistant/components/spc/binary_sensor.py index baa25266804ad3..6a0712d62bb58a 100644 --- a/homeassistant/components/spc/binary_sensor.py +++ b/homeassistant/components/spc/binary_sensor.py @@ -7,9 +7,10 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.core import callback -from homeassistant.components.spc import (DATA_API, SIGNAL_UPDATE_SENSOR) +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import DATA_API, SIGNAL_UPDATE_SENSOR _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index 0c8908a309f3de..f140f881ef4c7f 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -5,8 +5,6 @@ import voluptuous as vol from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.components.speedtestdotnet.const import ( - DATA_UPDATED, DOMAIN, SENSOR_TYPES) from homeassistant.const import ( CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL, CONF_UPDATE_INTERVAL, CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) @@ -15,6 +13,8 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval +from .const import DATA_UPDATED, DOMAIN, SENSOR_TYPES + REQUIREMENTS = ['speedtest-cli==2.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/speedtestdotnet/sensor.py b/homeassistant/components/speedtestdotnet/sensor.py index 11bc9a37ca01f9..fb92bb76ac848a 100644 --- a/homeassistant/components/speedtestdotnet/sensor.py +++ b/homeassistant/components/speedtestdotnet/sensor.py @@ -1,13 +1,14 @@ """Support for Speedtest.net internet speed testing sensor.""" import logging -from homeassistant.components.speedtestdotnet.const import ( - DATA_UPDATED, DOMAIN as SPEEDTESTDOTNET_DOMAIN, SENSOR_TYPES) from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity +from .const import ( + DATA_UPDATED, DOMAIN as SPEEDTESTDOTNET_DOMAIN, SENSOR_TYPES) + DEPENDENCIES = ['speedtestdotnet'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/spider/climate.py b/homeassistant/components/spider/climate.py index b3380ec8fb4b2a..3b612441a88466 100644 --- a/homeassistant/components/spider/climate.py +++ b/homeassistant/components/spider/climate.py @@ -4,12 +4,12 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - STATE_COOL, STATE_HEAT, STATE_IDLE, - SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_FAN_MODE) -from homeassistant.components.spider import DOMAIN as SPIDER_DOMAIN + STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_FAN_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from . import DOMAIN as SPIDER_DOMAIN + DEPENDENCIES = ['spider'] FAN_LIST = [ diff --git a/homeassistant/components/spider/switch.py b/homeassistant/components/spider/switch.py index 227e4748515e4c..e43762be460f1d 100644 --- a/homeassistant/components/spider/switch.py +++ b/homeassistant/components/spider/switch.py @@ -1,9 +1,10 @@ """Support for Spider switches.""" import logging -from homeassistant.components.spider import DOMAIN as SPIDER_DOMAIN from homeassistant.components.switch import SwitchDevice +from . import DOMAIN as SPIDER_DOMAIN + DEPENDENCIES = ['spider'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index d5f152bbd76d5e..71ebeadaeed022 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -1,13 +1,14 @@ """Support for Tado to create a climate device for each zone.""" import logging -from homeassistant.const import (PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ( + ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.util.temperature import convert as convert_temperature -from homeassistant.const import ATTR_TEMPERATURE -from homeassistant.components.tado import DATA_TADO + +from . import DATA_TADO _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tado/sensor.py b/homeassistant/components/tado/sensor.py index a1eb918ac5ded8..8fa858977a13ec 100644 --- a/homeassistant/components/tado/sensor.py +++ b/homeassistant/components/tado/sensor.py @@ -1,10 +1,11 @@ """Support for Tado sensors for each zone.""" import logging -from homeassistant.components.tado import DATA_TADO from homeassistant.const import ATTR_ID, ATTR_NAME, TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import DATA_TADO + _LOGGER = logging.getLogger(__name__) ATTR_DATA_ID = 'data_id' diff --git a/homeassistant/components/tahoma/binary_sensor.py b/homeassistant/components/tahoma/binary_sensor.py index 69855f7cb57de0..948c6f90a58895 100644 --- a/homeassistant/components/tahoma/binary_sensor.py +++ b/homeassistant/components/tahoma/binary_sensor.py @@ -1,12 +1,11 @@ """Support for Tahoma binary sensors.""" -import logging from datetime import timedelta +import logging + +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON -from homeassistant.components.binary_sensor import ( - BinarySensorDevice) -from homeassistant.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN, TahomaDevice) -from homeassistant.const import (STATE_OFF, STATE_ON, ATTR_BATTERY_LEVEL) +from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tahoma/cover.py b/homeassistant/components/tahoma/cover.py index 6dbf9a3980710b..85e785f9ca3b44 100644 --- a/homeassistant/components/tahoma/cover.py +++ b/homeassistant/components/tahoma/cover.py @@ -2,10 +2,10 @@ from datetime import timedelta import logging +from homeassistant.components.cover import ATTR_POSITION, CoverDevice from homeassistant.util.dt import utcnow -from homeassistant.components.cover import CoverDevice, ATTR_POSITION -from homeassistant.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN, TahomaDevice) + +from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tahoma/scene.py b/homeassistant/components/tahoma/scene.py index 643cc65aa1918d..eedb95d1a772c9 100644 --- a/homeassistant/components/tahoma/scene.py +++ b/homeassistant/components/tahoma/scene.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.scene import Scene -from homeassistant.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN) + +from . import DOMAIN as TAHOMA_DOMAIN DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tahoma/sensor.py b/homeassistant/components/tahoma/sensor.py index 8a2ea976ba7a9e..3c03911804ace1 100644 --- a/homeassistant/components/tahoma/sensor.py +++ b/homeassistant/components/tahoma/sensor.py @@ -1,11 +1,11 @@ """Support for Tahoma sensors.""" -import logging from datetime import timedelta +import logging -from homeassistant.helpers.entity import Entity -from homeassistant.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN, TahomaDevice) from homeassistant.const import ATTR_BATTERY_LEVEL +from homeassistant.helpers.entity import Entity + +from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tahoma/switch.py b/homeassistant/components/tahoma/switch.py index 779bff9ce0d4cc..71f00ed8937c7d 100644 --- a/homeassistant/components/tahoma/switch.py +++ b/homeassistant/components/tahoma/switch.py @@ -2,9 +2,9 @@ import logging from homeassistant.components.switch import SwitchDevice -from homeassistant.components.tahoma import ( - DOMAIN as TAHOMA_DOMAIN, TahomaDevice) -from homeassistant.const import (STATE_OFF, STATE_ON) +from homeassistant.const import STATE_OFF, STATE_ON + +from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice DEPENDENCIES = ['tahoma'] diff --git a/homeassistant/components/tcp/binary_sensor.py b/homeassistant/components/tcp/binary_sensor.py index 4a12febd871135..80d77cd52a168f 100644 --- a/homeassistant/components/tcp/binary_sensor.py +++ b/homeassistant/components/tcp/binary_sensor.py @@ -7,8 +7,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.tcp.sensor import ( - TcpSensor, CONF_VALUE_ON, PLATFORM_SCHEMA) + +from .sensor import CONF_VALUE_ON, PLATFORM_SCHEMA, TcpSensor _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/telegram_bot/broadcast.py b/homeassistant/components/telegram_bot/broadcast.py index eb52ab496d7bc2..a129ebf6604b7e 100644 --- a/homeassistant/components/telegram_bot/broadcast.py +++ b/homeassistant/components/telegram_bot/broadcast.py @@ -1,9 +1,7 @@ """Support for Telegram bot to send messages only.""" import logging -from homeassistant.components.telegram_bot import ( - initialize_bot, - PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA) +from . import PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA, initialize_bot _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/telegram_bot/polling.py b/homeassistant/components/telegram_bot/polling.py index 9936b69098568d..7d0039319e3a66 100644 --- a/homeassistant/components/telegram_bot/polling.py +++ b/homeassistant/components/telegram_bot/polling.py @@ -1,14 +1,14 @@ """Support for Telegram bot using polling.""" import logging -from homeassistant.components.telegram_bot import ( - initialize_bot, - CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity, - PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA) from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback +from . import ( + CONF_ALLOWED_CHAT_IDS, PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA, + BaseTelegramBotEntity, initialize_bot) + _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = TELEGRAM_PLATFORM_SCHEMA diff --git a/homeassistant/components/telegram_bot/webhooks.py b/homeassistant/components/telegram_bot/webhooks.py index 41a206944e7992..424ece81549e2f 100644 --- a/homeassistant/components/telegram_bot/webhooks.py +++ b/homeassistant/components/telegram_bot/webhooks.py @@ -7,14 +7,14 @@ from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.const import KEY_REAL_IP -from homeassistant.components.telegram_bot import ( - CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity, PLATFORM_SCHEMA, - initialize_bot) from homeassistant.const import ( - EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST, - HTTP_UNAUTHORIZED, CONF_URL) + CONF_URL, EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED) import homeassistant.helpers.config_validation as cv +from . import ( + CONF_ALLOWED_CHAT_IDS, PLATFORM_SCHEMA, BaseTelegramBotEntity, + initialize_bot) + DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tellduslive/binary_sensor.py b/homeassistant/components/tellduslive/binary_sensor.py index 85faeca96d4eca..fc13f75838ab9b 100644 --- a/homeassistant/components/tellduslive/binary_sensor.py +++ b/homeassistant/components/tellduslive/binary_sensor.py @@ -3,9 +3,10 @@ from homeassistant.components import binary_sensor, tellduslive from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .entry import TelldusLiveEntity + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tellduslive/cover.py b/homeassistant/components/tellduslive/cover.py index 1bd3158d100e16..6dac00ed7a26ac 100644 --- a/homeassistant/components/tellduslive/cover.py +++ b/homeassistant/components/tellduslive/cover.py @@ -3,9 +3,10 @@ from homeassistant.components import cover, tellduslive from homeassistant.components.cover import CoverDevice -from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .entry import TelldusLiveEntity + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tellduslive/light.py b/homeassistant/components/tellduslive/light.py index 12baf8384f6360..3847c66b6cbdd2 100644 --- a/homeassistant/components/tellduslive/light.py +++ b/homeassistant/components/tellduslive/light.py @@ -4,9 +4,10 @@ from homeassistant.components import light, tellduslive from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .entry import TelldusLiveEntity + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tellduslive/sensor.py b/homeassistant/components/tellduslive/sensor.py index 42c93aa52f9765..156c11c95a7299 100644 --- a/homeassistant/components/tellduslive/sensor.py +++ b/homeassistant/components/tellduslive/sensor.py @@ -2,12 +2,13 @@ import logging from homeassistant.components import sensor, tellduslive -from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.const import ( DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, - TEMP_CELSIUS, POWER_WATT) + POWER_WATT, TEMP_CELSIUS) from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .entry import TelldusLiveEntity + _LOGGER = logging.getLogger(__name__) SENSOR_TYPE_TEMPERATURE = 'temp' diff --git a/homeassistant/components/tellduslive/switch.py b/homeassistant/components/tellduslive/switch.py index bb0164b10bb21c..55275b5b75405b 100644 --- a/homeassistant/components/tellduslive/switch.py +++ b/homeassistant/components/tellduslive/switch.py @@ -2,10 +2,11 @@ import logging from homeassistant.components import switch, tellduslive -from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import ToggleEntity +from .entry import TelldusLiveEntity + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tellstick/cover.py b/homeassistant/components/tellstick/cover.py index d0c9c031435c27..b90e34229fd691 100644 --- a/homeassistant/components/tellstick/cover.py +++ b/homeassistant/components/tellstick/cover.py @@ -1,8 +1,9 @@ """Support for Tellstick covers.""" from homeassistant.components.cover import CoverDevice -from homeassistant.components.tellstick import ( - DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES, ATTR_DISCOVER_CONFIG, - DATA_TELLSTICK, TellstickDevice) + +from . import ( + ATTR_DISCOVER_CONFIG, ATTR_DISCOVER_DEVICES, DATA_TELLSTICK, + DEFAULT_SIGNAL_REPETITIONS, TellstickDevice) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/tellstick/light.py b/homeassistant/components/tellstick/light.py index 5deee5e08a62a8..15c8b0c5eb9732 100644 --- a/homeassistant/components/tellstick/light.py +++ b/homeassistant/components/tellstick/light.py @@ -1,10 +1,10 @@ """Support for Tellstick lights.""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.tellstick import ( - DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES, ATTR_DISCOVER_CONFIG, - DATA_TELLSTICK, TellstickDevice) +from . import ( + ATTR_DISCOVER_CONFIG, ATTR_DISCOVER_DEVICES, DATA_TELLSTICK, + DEFAULT_SIGNAL_REPETITIONS, TellstickDevice) SUPPORT_TELLSTICK = SUPPORT_BRIGHTNESS diff --git a/homeassistant/components/tellstick/switch.py b/homeassistant/components/tellstick/switch.py index 56d563e494c622..75c18bee8c575a 100644 --- a/homeassistant/components/tellstick/switch.py +++ b/homeassistant/components/tellstick/switch.py @@ -1,9 +1,10 @@ """Support for Tellstick switches.""" -from homeassistant.components.tellstick import ( - DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES, - ATTR_DISCOVER_CONFIG, DATA_TELLSTICK, TellstickDevice) from homeassistant.helpers.entity import ToggleEntity +from . import ( + ATTR_DISCOVER_CONFIG, ATTR_DISCOVER_DEVICES, DATA_TELLSTICK, + DEFAULT_SIGNAL_REPETITIONS, TellstickDevice) + def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick switches.""" diff --git a/homeassistant/components/tesla/binary_sensor.py b/homeassistant/components/tesla/binary_sensor.py index 2c037140f0a6d1..a87239d24308d9 100644 --- a/homeassistant/components/tesla/binary_sensor.py +++ b/homeassistant/components/tesla/binary_sensor.py @@ -2,8 +2,9 @@ import logging from homeassistant.components.binary_sensor import ( - BinarySensorDevice, ENTITY_ID_FORMAT) -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN, TeslaDevice + ENTITY_ID_FORMAT, BinarySensorDevice) + +from . import DOMAIN as TESLA_DOMAIN, TeslaDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tesla/climate.py b/homeassistant/components/tesla/climate.py index 118e7204bca07a..603ce1a4d61785 100644 --- a/homeassistant/components/tesla/climate.py +++ b/homeassistant/components/tesla/climate.py @@ -1,14 +1,14 @@ """Support for Tesla HVAC system.""" import logging -from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT +from homeassistant.components.climate import ENTITY_ID_FORMAT, ClimateDevice from homeassistant.components.climate.const import ( SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN -from homeassistant.components.tesla import TeslaDevice from homeassistant.const import ( ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT) +from . import DOMAIN as TESLA_DOMAIN, TeslaDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tesla'] diff --git a/homeassistant/components/tesla/device_tracker.py b/homeassistant/components/tesla/device_tracker.py index 0aeab5b1c7d2cd..5a7693d8370665 100644 --- a/homeassistant/components/tesla/device_tracker.py +++ b/homeassistant/components/tesla/device_tracker.py @@ -1,10 +1,11 @@ """Support for tracking Tesla cars.""" import logging -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN from homeassistant.helpers.event import track_utc_time_change from homeassistant.util import slugify +from . import DOMAIN as TESLA_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tesla'] diff --git a/homeassistant/components/tesla/lock.py b/homeassistant/components/tesla/lock.py index 34d660ac83c6a4..ade394496d6edb 100644 --- a/homeassistant/components/tesla/lock.py +++ b/homeassistant/components/tesla/lock.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.lock import ENTITY_ID_FORMAT, LockDevice -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN -from homeassistant.components.tesla import TeslaDevice from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED +from . import DOMAIN as TESLA_DOMAIN, TeslaDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tesla'] diff --git a/homeassistant/components/tesla/sensor.py b/homeassistant/components/tesla/sensor.py index 1d4505ed9a482a..99705d3f79336e 100644 --- a/homeassistant/components/tesla/sensor.py +++ b/homeassistant/components/tesla/sensor.py @@ -3,12 +3,12 @@ import logging from homeassistant.components.sensor import ENTITY_ID_FORMAT -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN -from homeassistant.components.tesla import TeslaDevice from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, LENGTH_KILOMETERS, LENGTH_MILES) + LENGTH_KILOMETERS, LENGTH_MILES, TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import Entity +from . import DOMAIN as TESLA_DOMAIN, TeslaDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tesla'] diff --git a/homeassistant/components/tesla/switch.py b/homeassistant/components/tesla/switch.py index a1787e9993ed99..e00164ff1a7d52 100644 --- a/homeassistant/components/tesla/switch.py +++ b/homeassistant/components/tesla/switch.py @@ -2,10 +2,10 @@ import logging from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice -from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN -from homeassistant.components.tesla import TeslaDevice from homeassistant.const import STATE_OFF, STATE_ON +from . import DOMAIN as TESLA_DOMAIN, TeslaDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tesla'] diff --git a/homeassistant/components/thethingsnetwork/sensor.py b/homeassistant/components/thethingsnetwork/sensor.py index 05da90bf7ac771..d59b429721b562 100644 --- a/homeassistant/components/thethingsnetwork/sensor.py +++ b/homeassistant/components/thethingsnetwork/sensor.py @@ -8,13 +8,13 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.thethingsnetwork import ( - DATA_TTN, TTN_APP_ID, TTN_ACCESS_KEY, TTN_DATA_STORAGE_URL) from homeassistant.const import CONTENT_TYPE_JSON from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import DATA_TTN, TTN_ACCESS_KEY, TTN_APP_ID, TTN_DATA_STORAGE_URL + _LOGGER = logging.getLogger(__name__) ATTR_DEVICE_ID = 'device_id' diff --git a/homeassistant/components/tibber/notify.py b/homeassistant/components/tibber/notify.py index 6ae22c342095e9..604fadb870c5f9 100644 --- a/homeassistant/components/tibber/notify.py +++ b/homeassistant/components/tibber/notify.py @@ -4,7 +4,8 @@ from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService) -from homeassistant.components.tibber import DOMAIN as TIBBER_DOMAIN + +from . import DOMAIN as TIBBER_DOMAIN _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index fb224a25468b3b..9f3e9cdcc62dbb 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -1,16 +1,15 @@ """Support for Tibber sensors.""" import asyncio - +from datetime import timedelta import logging -from datetime import timedelta import aiohttp -from homeassistant.components.tibber import DOMAIN as TIBBER_DOMAIN from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity import Entity -from homeassistant.util import dt as dt_util -from homeassistant.util import Throttle +from homeassistant.util import Throttle, dt as dt_util + +from . import DOMAIN as TIBBER_DOMAIN _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index de1a943c33a003..0ba1dfaa33a2c3 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -8,15 +8,14 @@ import time from homeassistant.components.light import ( - Light, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, - SUPPORT_COLOR_TEMP, SUPPORT_COLOR) -from homeassistant.util.color import \ - color_temperature_mired_to_kelvin as mired_to_kelvin -from homeassistant.util.color import ( - color_temperature_kelvin_to_mired as kelvin_to_mired) + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) import homeassistant.helpers.device_registry as dr -from homeassistant.components.tplink import (DOMAIN as TPLINK_DOMAIN, - CONF_LIGHT) +from homeassistant.util.color import ( + color_temperature_kelvin_to_mired as kelvin_to_mired, + color_temperature_mired_to_kelvin as mired_to_kelvin) + +from . import CONF_LIGHT, DOMAIN as TPLINK_DOMAIN DEPENDENCIES = ['tplink'] diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index 65b884169c7916..a75945e99565e2 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -8,12 +8,12 @@ import time from homeassistant.components.switch import ( - SwitchDevice, ATTR_CURRENT_POWER_W, ATTR_TODAY_ENERGY_KWH) -from homeassistant.components.tplink import (DOMAIN as TPLINK_DOMAIN, - CONF_SWITCH) + ATTR_CURRENT_POWER_W, ATTR_TODAY_ENERGY_KWH, SwitchDevice) from homeassistant.const import ATTR_VOLTAGE import homeassistant.helpers.device_registry as dr +from . import CONF_SWITCH, DOMAIN as TPLINK_DOMAIN + DEPENDENCIES = ['tplink'] PARALLEL_UPDATES = 0 diff --git a/homeassistant/components/tradfri/light.py b/homeassistant/components/tradfri/light.py index e5e27ecbed1e1d..38ce428b51b7c7 100644 --- a/homeassistant/components/tradfri/light.py +++ b/homeassistant/components/tradfri/light.py @@ -1,19 +1,16 @@ """Support for IKEA Tradfri lights.""" import logging -from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION, - SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP, - SUPPORT_COLOR, Light) -from homeassistant.components.light import \ - PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA -from homeassistant.components.tradfri import ( - KEY_GATEWAY, KEY_API, DOMAIN as TRADFRI_DOMAIN) -from homeassistant.components.tradfri.const import ( - CONF_IMPORT_GROUPS, CONF_GATEWAY_ID) + PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_TRANSITION, Light) +from homeassistant.core import callback import homeassistant.util.color as color_util +from . import DOMAIN as TRADFRI_DOMAIN, KEY_API, KEY_GATEWAY +from .const import CONF_GATEWAY_ID, CONF_IMPORT_GROUPS + _LOGGER = logging.getLogger(__name__) ATTR_DIMMER = 'dimmer' diff --git a/homeassistant/components/tradfri/sensor.py b/homeassistant/components/tradfri/sensor.py index 97c7dc9627d608..acc84a935904d3 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -1,12 +1,12 @@ """Support for IKEA Tradfri sensors.""" -import logging - from datetime import timedelta +import logging from homeassistant.core import callback -from homeassistant.components.tradfri import KEY_GATEWAY, KEY_API from homeassistant.helpers.entity import Entity +from . import KEY_API, KEY_GATEWAY + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['tradfri'] diff --git a/homeassistant/components/tradfri/switch.py b/homeassistant/components/tradfri/switch.py index 23e6cb20c8fdd2..ef9a9537cffe18 100644 --- a/homeassistant/components/tradfri/switch.py +++ b/homeassistant/components/tradfri/switch.py @@ -1,12 +1,11 @@ """Support for IKEA Tradfri switches.""" import logging -from homeassistant.core import callback from homeassistant.components.switch import SwitchDevice -from homeassistant.components.tradfri import ( - KEY_GATEWAY, KEY_API, DOMAIN as TRADFRI_DOMAIN) -from homeassistant.components.tradfri.const import ( - CONF_GATEWAY_ID) +from homeassistant.core import callback + +from . import DOMAIN as TRADFRI_DOMAIN, KEY_API, KEY_GATEWAY +from .const import CONF_GATEWAY_ID _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/transmission/sensor.py b/homeassistant/components/transmission/sensor.py index 061ed2c0c641fe..dfd4c1950977b0 100644 --- a/homeassistant/components/transmission/sensor.py +++ b/homeassistant/components/transmission/sensor.py @@ -1,15 +1,14 @@ """Support for monitoring the Transmission BitTorrent client API.""" from datetime import timedelta - import logging -from homeassistant.components.transmission import ( - DATA_TRANSMISSION, SENSOR_TYPES, DATA_UPDATED) from homeassistant.const import STATE_IDLE from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +from . import DATA_TRANSMISSION, DATA_UPDATED, SENSOR_TYPES + DEPENDENCIES = ['transmission'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/transmission/switch.py b/homeassistant/components/transmission/switch.py index 373397eddd607c..854a2e727b0b83 100644 --- a/homeassistant/components/transmission/switch.py +++ b/homeassistant/components/transmission/switch.py @@ -1,14 +1,13 @@ """Support for setting the Transmission BitTorrent client Turtle Mode.""" import logging -from homeassistant.components.transmission import ( - DATA_TRANSMISSION, DATA_UPDATED) -from homeassistant.const import ( - STATE_OFF, STATE_ON) +from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import ToggleEntity +from . import DATA_TRANSMISSION, DATA_UPDATED + DEPENDENCIES = ['transmission'] _LOGGING = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/amazon_polly.py b/homeassistant/components/tts/amazon_polly.py index 0102a1fec09ef1..12383df115a33c 100644 --- a/homeassistant/components/tts/amazon_polly.py +++ b/homeassistant/components/tts/amazon_polly.py @@ -8,9 +8,10 @@ import voluptuous as vol -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA import homeassistant.helpers.config_validation as cv +from . import PLATFORM_SCHEMA, Provider + REQUIREMENTS = ['boto3==1.9.16'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/baidu.py b/homeassistant/components/tts/baidu.py index 609f38454fe2d5..e7a1f368f1daf3 100644 --- a/homeassistant/components/tts/baidu.py +++ b/homeassistant/components/tts/baidu.py @@ -6,12 +6,14 @@ """ import logging + import voluptuous as vol -from homeassistant.components.tts import Provider, CONF_LANG, PLATFORM_SCHEMA from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv +from . import CONF_LANG, PLATFORM_SCHEMA, Provider + REQUIREMENTS = ["baidu-aip==1.6.6"] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/demo.py b/homeassistant/components/tts/demo.py index ba854fc2f5eedd..6784e7cea61c4e 100644 --- a/homeassistant/components/tts/demo.py +++ b/homeassistant/components/tts/demo.py @@ -8,7 +8,7 @@ import voluptuous as vol -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG +from . import CONF_LANG, PLATFORM_SCHEMA, Provider SUPPORT_LANGUAGES = [ 'en', 'de' diff --git a/homeassistant/components/tts/marytts.py b/homeassistant/components/tts/marytts.py index 61f01a9b292fe7..971d3fb5705d7b 100644 --- a/homeassistant/components/tts/marytts.py +++ b/homeassistant/components/tts/marytts.py @@ -13,10 +13,11 @@ import voluptuous as vol from homeassistant.const import CONF_HOST, CONF_PORT -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from . import CONF_LANG, PLATFORM_SCHEMA, Provider + _LOGGER = logging.getLogger(__name__) SUPPORT_LANGUAGES = [ diff --git a/homeassistant/components/tts/microsoft.py b/homeassistant/components/tts/microsoft.py index 3cce7c1a78d77c..ab9fb576c2856b 100644 --- a/homeassistant/components/tts/microsoft.py +++ b/homeassistant/components/tts/microsoft.py @@ -4,15 +4,16 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/tts.microsoft/ """ -import logging from http.client import HTTPException +import logging import voluptuous as vol -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG -from homeassistant.const import CONF_TYPE, CONF_API_KEY +from homeassistant.const import CONF_API_KEY, CONF_TYPE import homeassistant.helpers.config_validation as cv +from . import CONF_LANG, PLATFORM_SCHEMA, Provider + CONF_GENDER = 'gender' CONF_OUTPUT = 'output' CONF_RATE = 'rate' diff --git a/homeassistant/components/tts/picotts.py b/homeassistant/components/tts/picotts.py index 59c698303ad1e3..99d3b5e9786516 100644 --- a/homeassistant/components/tts/picotts.py +++ b/homeassistant/components/tts/picotts.py @@ -4,14 +4,15 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/tts.picotts/ """ +import logging import os -import tempfile import shutil import subprocess -import logging +import tempfile + import voluptuous as vol -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG +from . import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/voicerss.py b/homeassistant/components/tts/voicerss.py index 22eba69e510c7e..3676dff3bc641b 100644 --- a/homeassistant/components/tts/voicerss.py +++ b/homeassistant/components/tts/voicerss.py @@ -12,10 +12,11 @@ import voluptuous as vol from homeassistant.const import CONF_API_KEY -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from . import CONF_LANG, PLATFORM_SCHEMA, Provider + _LOGGER = logging.getLogger(__name__) VOICERSS_API_URL = "https://api.voicerss.org/" diff --git a/homeassistant/components/tts/yandextts.py b/homeassistant/components/tts/yandextts.py index 112e78413ed0ed..aecba2925ddfe6 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/tts/yandextts.py @@ -12,10 +12,10 @@ import voluptuous as vol from homeassistant.const import CONF_API_KEY -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from . import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 06714760a024ff..b7a10dad8626c3 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -1,15 +1,15 @@ """Support for the Tuya climate devices.""" -from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT +from homeassistant.components.climate import ENTITY_ID_FORMAT, ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, STATE_COOL, STATE_ECO, - STATE_FAN_ONLY, STATE_HEAT, SUPPORT_FAN_MODE, SUPPORT_ON_OFF, - SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.fan import SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice - + STATE_AUTO, STATE_COOL, STATE_ECO, STATE_FAN_ONLY, STATE_HEAT, + SUPPORT_FAN_MODE, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) +from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS, TEMP_FAHRENHEIT) +from . import DATA_TUYA, TuyaDevice + DEPENDENCIES = ['tuya'] DEVICE_TYPE = 'climate' diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index ac2309cbf9e54a..274f4d9386936f 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -1,7 +1,8 @@ """Support for Tuya covers.""" from homeassistant.components.cover import ( - CoverDevice, ENTITY_ID_FORMAT, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP) -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice + ENTITY_ID_FORMAT, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, CoverDevice) + +from . import DATA_TUYA, TuyaDevice DEPENDENCIES = ['tuya'] diff --git a/homeassistant/components/tuya/fan.py b/homeassistant/components/tuya/fan.py index b6e2cb6950c5c2..259417869dc2dc 100644 --- a/homeassistant/components/tuya/fan.py +++ b/homeassistant/components/tuya/fan.py @@ -1,9 +1,10 @@ """Support for Tuya fans.""" from homeassistant.components.fan import ( - ENTITY_ID_FORMAT, FanEntity, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED) -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice + ENTITY_ID_FORMAT, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity) from homeassistant.const import STATE_OFF +from . import DATA_TUYA, TuyaDevice + DEPENDENCIES = ['tuya'] diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 1cf2f811872dc9..17f9b43dcbebc6 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -1,11 +1,11 @@ """Support for the Tuya lights.""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ENTITY_ID_FORMAT, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_COLOR, Light) - -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice + SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) from homeassistant.util import color as colorutil +from . import DATA_TUYA, TuyaDevice + DEPENDENCIES = ['tuya'] diff --git a/homeassistant/components/tuya/scene.py b/homeassistant/components/tuya/scene.py index 33d207d85456e2..24383dca6e4c11 100644 --- a/homeassistant/components/tuya/scene.py +++ b/homeassistant/components/tuya/scene.py @@ -1,6 +1,7 @@ """Support for the Tuya scenes.""" -from homeassistant.components.scene import Scene, DOMAIN -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice +from homeassistant.components.scene import DOMAIN, Scene + +from . import DATA_TUYA, TuyaDevice DEPENDENCIES = ['tuya'] diff --git a/homeassistant/components/tuya/switch.py b/homeassistant/components/tuya/switch.py index 1e8fab2cc1ba01..c2e32eedc59164 100644 --- a/homeassistant/components/tuya/switch.py +++ b/homeassistant/components/tuya/switch.py @@ -1,6 +1,7 @@ """Support for Tuya switches.""" from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice -from homeassistant.components.tuya import DATA_TUYA, TuyaDevice + +from . import DATA_TUYA, TuyaDevice DEPENDENCIES = ['tuya'] diff --git a/homeassistant/components/unifi/const.py b/homeassistant/components/unifi/const.py index 8fe90823c492ae..fdeb15ee4adfcb 100644 --- a/homeassistant/components/unifi/const.py +++ b/homeassistant/components/unifi/const.py @@ -1,7 +1,7 @@ """Constants for the UniFi component.""" import logging -LOGGER = logging.getLogger('homeassistant.components.unifi') +LOGGER = logging.getLogger('.') DOMAIN = 'unifi' CONTROLLER_ID = '{host}-{site}' diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 425b9878f6de3a..e90da2dbcd85fe 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -1,19 +1,18 @@ """Support for devices connected to UniFi POE.""" import asyncio -import logging - from datetime import timedelta +import logging import async_timeout from homeassistant.components import unifi from homeassistant.components.switch import SwitchDevice -from homeassistant.components.unifi.const import ( - CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID, DOMAIN) from homeassistant.const import CONF_HOST from homeassistant.core import callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from .const import CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID, DOMAIN + DEPENDENCIES = [DOMAIN] SCAN_INTERVAL = timedelta(seconds=15) diff --git a/homeassistant/components/upcloud/binary_sensor.py b/homeassistant/components/upcloud/binary_sensor.py index 3fd54b349a2995..a0c3c9f34c6fdc 100644 --- a/homeassistant/components/upcloud/binary_sensor.py +++ b/homeassistant/components/upcloud/binary_sensor.py @@ -3,11 +3,11 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.upcloud import ( - UpCloudServerEntity, CONF_SERVERS, DATA_UPCLOUD) + PLATFORM_SCHEMA, BinarySensorDevice) +import homeassistant.helpers.config_validation as cv + +from . import CONF_SERVERS, DATA_UPCLOUD, UpCloudServerEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/upcloud/switch.py b/homeassistant/components/upcloud/switch.py index 0b44d787f6fbe2..7e84adccf5536e 100644 --- a/homeassistant/components/upcloud/switch.py +++ b/homeassistant/components/upcloud/switch.py @@ -3,11 +3,11 @@ import voluptuous as vol +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import STATE_OFF import homeassistant.helpers.config_validation as cv -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.components.upcloud import ( - UpCloudServerEntity, CONF_SERVERS, DATA_UPCLOUD) + +from . import CONF_SERVERS, DATA_UPCLOUD, UpCloudServerEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/upnp/const.py b/homeassistant/components/upnp/const.py index 04932488acd2a2..9d2957660dc687 100644 --- a/homeassistant/components/upnp/const.py +++ b/homeassistant/components/upnp/const.py @@ -1,12 +1,11 @@ """Constants for the IGD component.""" import logging - CONF_ENABLE_PORT_MAPPING = 'port_mapping' CONF_ENABLE_SENSORS = 'sensors' CONF_HASS = 'hass' CONF_LOCAL_IP = 'local_ip' CONF_PORTS = 'ports' DOMAIN = 'upnp' -LOGGER = logging.getLogger('homeassistant.components.upnp') +LOGGER = logging.getLogger('.') SIGNAL_REMOVE_SENSOR = 'upnp_remove_sensor' diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index 8eccf8834a19c0..5f544e1a134fb3 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -10,9 +10,8 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from homeassistant.components.upnp.const import DOMAIN as DOMAIN_UPNP -from homeassistant.components.upnp.const import SIGNAL_REMOVE_SENSOR +from .const import DOMAIN as DOMAIN_UPNP, SIGNAL_REMOVE_SENSOR _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/usps/camera.py b/homeassistant/components/usps/camera.py index d4769102d14424..5b5eaca4ce293a 100644 --- a/homeassistant/components/usps/camera.py +++ b/homeassistant/components/usps/camera.py @@ -3,7 +3,8 @@ import logging from homeassistant.components.camera import Camera -from homeassistant.components.usps import DATA_USPS + +from . import DATA_USPS _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/usps/sensor.py b/homeassistant/components/usps/sensor.py index 1603715861d3e0..3e5fea5c4ee7f0 100644 --- a/homeassistant/components/usps/sensor.py +++ b/homeassistant/components/usps/sensor.py @@ -2,12 +2,13 @@ from collections import defaultdict import logging -from homeassistant.components.usps import DATA_USPS from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DATE from homeassistant.helpers.entity import Entity from homeassistant.util import slugify from homeassistant.util.dt import now +from . import DATA_USPS + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['usps'] diff --git a/homeassistant/components/vacuum/demo.py b/homeassistant/components/vacuum/demo.py index 12507683f516e3..2a19337c0db9be 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/vacuum/demo.py @@ -6,13 +6,12 @@ """ import logging -from homeassistant.components.vacuum import ( - ATTR_CLEANED_AREA, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, - SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, - SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, - SUPPORT_TURN_ON, SUPPORT_STATE, SUPPORT_START, STATE_CLEANING, - STATE_DOCKED, STATE_IDLE, STATE_PAUSED, STATE_RETURNING, VacuumDevice, - StateVacuumDevice) +from . import ( + ATTR_CLEANED_AREA, STATE_CLEANING, STATE_DOCKED, STATE_IDLE, STATE_PAUSED, + STATE_RETURNING, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, + SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, + SUPPORT_START, SUPPORT_STATE, SUPPORT_STATUS, SUPPORT_STOP, + SUPPORT_TURN_OFF, SUPPORT_TURN_ON, StateVacuumDevice, VacuumDevice) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/velbus/binary_sensor.py b/homeassistant/components/velbus/binary_sensor.py index 43ffa232b40e7d..cbe1350bd4f667 100644 --- a/homeassistant/components/velbus/binary_sensor.py +++ b/homeassistant/components/velbus/binary_sensor.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.velbus import ( - DOMAIN as VELBUS_DOMAIN, VelbusEntity) + +from . import DOMAIN as VELBUS_DOMAIN, VelbusEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index 1f45408a666c97..470524bb6f3dff 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -4,10 +4,9 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( STATE_HEAT, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.velbus import ( - DOMAIN as VELBUS_DOMAIN, VelbusEntity) -from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT + +from . import DOMAIN as VELBUS_DOMAIN, VelbusEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/velbus/cover.py b/homeassistant/components/velbus/cover.py index 72a5a7af79ba9d..b176ab76c4b74f 100644 --- a/homeassistant/components/velbus/cover.py +++ b/homeassistant/components/velbus/cover.py @@ -5,12 +5,12 @@ import voluptuous as vol from homeassistant.components.cover import ( - CoverDevice, PLATFORM_SCHEMA, SUPPORT_OPEN, SUPPORT_CLOSE, - SUPPORT_STOP) -from homeassistant.components.velbus import DOMAIN -from homeassistant.const import (CONF_COVERS, CONF_NAME) + PLATFORM_SCHEMA, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, CoverDevice) +from homeassistant.const import CONF_COVERS, CONF_NAME import homeassistant.helpers.config_validation as cv +from . import DOMAIN + _LOGGER = logging.getLogger(__name__) COVER_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/velbus/sensor.py b/homeassistant/components/velbus/sensor.py index 10ad89ab847f5e..ad78a795a30b56 100644 --- a/homeassistant/components/velbus/sensor.py +++ b/homeassistant/components/velbus/sensor.py @@ -1,8 +1,7 @@ """Support for Velbus sensors.""" import logging -from homeassistant.components.velbus import ( - DOMAIN as VELBUS_DOMAIN, VelbusEntity) +from . import DOMAIN as VELBUS_DOMAIN, VelbusEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/velbus/switch.py b/homeassistant/components/velbus/switch.py index 7104bb0750d0d8..b5ef89ca48031d 100644 --- a/homeassistant/components/velbus/switch.py +++ b/homeassistant/components/velbus/switch.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.switch import SwitchDevice -from homeassistant.components.velbus import ( - DOMAIN as VELBUS_DOMAIN, VelbusEntity) + +from . import DOMAIN as VELBUS_DOMAIN, VelbusEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index 6abaa42bb9d4d3..1893909b70608c 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -2,9 +2,10 @@ from homeassistant.components.cover import ( ATTR_POSITION, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION, SUPPORT_STOP, CoverDevice) -from homeassistant.components.velux import DATA_VELUX from homeassistant.core import callback +from . import DATA_VELUX + DEPENDENCIES = ['velux'] diff --git a/homeassistant/components/velux/scene.py b/homeassistant/components/velux/scene.py index b0716dc2cb87ea..614d3f349a2366 100644 --- a/homeassistant/components/velux/scene.py +++ b/homeassistant/components/velux/scene.py @@ -1,6 +1,7 @@ """Support for VELUX scenes.""" from homeassistant.components.scene import Scene -from homeassistant.components.velux import _LOGGER, DATA_VELUX + +from . import _LOGGER, DATA_VELUX DEPENDENCIES = ['velux'] diff --git a/homeassistant/components/vera/binary_sensor.py b/homeassistant/components/vera/binary_sensor.py index 837422dbc7c453..c81fa31938f554 100644 --- a/homeassistant/components/vera/binary_sensor.py +++ b/homeassistant/components/vera/binary_sensor.py @@ -2,9 +2,9 @@ import logging from homeassistant.components.binary_sensor import ( - BinarySensorDevice, ENTITY_ID_FORMAT) -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) + ENTITY_ID_FORMAT, BinarySensorDevice) + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice DEPENDENCIES = ['vera'] diff --git a/homeassistant/components/vera/climate.py b/homeassistant/components/vera/climate.py index 9c812da9208ee4..f8ff9c21b89aae 100644 --- a/homeassistant/components/vera/climate.py +++ b/homeassistant/components/vera/climate.py @@ -1,21 +1,15 @@ """Support for Vera thermostats.""" import logging -from homeassistant.util import convert -from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT +from homeassistant.components.climate import ENTITY_ID_FORMAT, ClimateDevice from homeassistant.components.climate.const import ( - STATE_AUTO, STATE_COOL, - STATE_HEAT, SUPPORT_TARGET_TEMPERATURE, - SUPPORT_OPERATION_MODE, SUPPORT_FAN_MODE) + STATE_AUTO, STATE_COOL, STATE_HEAT, SUPPORT_FAN_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - STATE_ON, - STATE_OFF, - TEMP_FAHRENHEIT, - TEMP_CELSIUS, - ATTR_TEMPERATURE) - -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) + ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT) +from homeassistant.util import convert + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice DEPENDENCIES = ['vera'] diff --git a/homeassistant/components/vera/cover.py b/homeassistant/components/vera/cover.py index 1168cca8425096..4cf2aac3bb4e3c 100644 --- a/homeassistant/components/vera/cover.py +++ b/homeassistant/components/vera/cover.py @@ -1,10 +1,10 @@ """Support for Vera cover - curtains, rollershutters etc.""" import logging -from homeassistant.components.cover import CoverDevice, ENTITY_ID_FORMAT, \ - ATTR_POSITION -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) +from homeassistant.components.cover import ( + ATTR_POSITION, ENTITY_ID_FORMAT, CoverDevice) + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice DEPENDENCIES = ['vera'] diff --git a/homeassistant/components/vera/light.py b/homeassistant/components/vera/light.py index 93e54b915c7473..e4e315bb52e87d 100644 --- a/homeassistant/components/vera/light.py +++ b/homeassistant/components/vera/light.py @@ -2,12 +2,12 @@ import logging from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_HS_COLOR, ENTITY_ID_FORMAT, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) + ATTR_BRIGHTNESS, ATTR_HS_COLOR, ENTITY_ID_FORMAT, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, Light) import homeassistant.util.color as color_util +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['vera'] diff --git a/homeassistant/components/vera/lock.py b/homeassistant/components/vera/lock.py index 61d5f0baf287bd..5ace07b87d7087 100644 --- a/homeassistant/components/vera/lock.py +++ b/homeassistant/components/vera/lock.py @@ -2,9 +2,9 @@ import logging from homeassistant.components.lock import ENTITY_ID_FORMAT, LockDevice -from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED) -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) +from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vera/scene.py b/homeassistant/components/vera/scene.py index 0960512f6d19b6..5000f9bc50f9c8 100644 --- a/homeassistant/components/vera/scene.py +++ b/homeassistant/components/vera/scene.py @@ -1,10 +1,10 @@ """Support for Vera scenes.""" import logging -from homeassistant.util import slugify from homeassistant.components.scene import Scene -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_SCENES, VERA_ID_FORMAT) +from homeassistant.util import slugify + +from . import VERA_CONTROLLER, VERA_ID_FORMAT, VERA_SCENES _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vera/sensor.py b/homeassistant/components/vera/sensor.py index 8b68cc9190f597..3c026046b3eea0 100644 --- a/homeassistant/components/vera/sensor.py +++ b/homeassistant/components/vera/sensor.py @@ -1,14 +1,13 @@ """Support for Vera sensors.""" -import logging from datetime import timedelta +import logging -from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT) -from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import ENTITY_ID_FORMAT +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.helpers.entity import Entity from homeassistant.util import convert -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vera/switch.py b/homeassistant/components/vera/switch.py index 2f4d18e34e13ed..f422e49bf42f8e 100644 --- a/homeassistant/components/vera/switch.py +++ b/homeassistant/components/vera/switch.py @@ -1,10 +1,10 @@ """Support for Vera switches.""" import logging -from homeassistant.util import convert from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice -from homeassistant.components.vera import ( - VERA_CONTROLLER, VERA_DEVICES, VeraDevice) +from homeassistant.util import convert + +from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/alarm_control_panel.py b/homeassistant/components/verisure/alarm_control_panel.py index adcdcd668cb6f1..dc73be056dbe1e 100644 --- a/homeassistant/components/verisure/alarm_control_panel.py +++ b/homeassistant/components/verisure/alarm_control_panel.py @@ -3,11 +3,11 @@ from time import sleep import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.verisure import CONF_ALARM, CONF_CODE_DIGITS -from homeassistant.components.verisure import HUB as hub from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) +from . import CONF_ALARM, CONF_CODE_DIGITS, HUB as hub + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/binary_sensor.py b/homeassistant/components/verisure/binary_sensor.py index 4c9e79724fee1e..1c1e0ee15c3360 100644 --- a/homeassistant/components/verisure/binary_sensor.py +++ b/homeassistant/components/verisure/binary_sensor.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.verisure import CONF_DOOR_WINDOW -from homeassistant.components.verisure import HUB as hub + +from . import CONF_DOOR_WINDOW, HUB as hub _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/camera.py b/homeassistant/components/verisure/camera.py index 7112e535a95fca..fad3d2bef045d1 100644 --- a/homeassistant/components/verisure/camera.py +++ b/homeassistant/components/verisure/camera.py @@ -5,8 +5,8 @@ from homeassistant.components.camera import Camera from homeassistant.const import EVENT_HOMEASSISTANT_STOP -from homeassistant.components.verisure import HUB as hub -from homeassistant.components.verisure import CONF_SMARTCAM + +from . import CONF_SMARTCAM, HUB as hub _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/lock.py b/homeassistant/components/verisure/lock.py index cdd230ea7f77c7..2010504c990292 100644 --- a/homeassistant/components/verisure/lock.py +++ b/homeassistant/components/verisure/lock.py @@ -1,13 +1,11 @@ """Support for Verisure locks.""" import logging -from time import sleep -from time import time -from homeassistant.components.verisure import HUB as hub -from homeassistant.components.verisure import ( - CONF_LOCKS, CONF_DEFAULT_LOCK_CODE, CONF_CODE_DIGITS) +from time import sleep, time + from homeassistant.components.lock import LockDevice -from homeassistant.const import ( - ATTR_CODE, STATE_LOCKED, STATE_UNLOCKED) +from homeassistant.const import ATTR_CODE, STATE_LOCKED, STATE_UNLOCKED + +from . import CONF_CODE_DIGITS, CONF_DEFAULT_LOCK_CODE, CONF_LOCKS, HUB as hub _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/sensor.py b/homeassistant/components/verisure/sensor.py index 13706d8408f77e..cf5205a61169ff 100644 --- a/homeassistant/components/verisure/sensor.py +++ b/homeassistant/components/verisure/sensor.py @@ -1,12 +1,11 @@ """Support for Verisure sensors.""" import logging -from homeassistant.components.verisure import HUB as hub -from homeassistant.components.verisure import ( - CONF_THERMOMETERS, CONF_HYDROMETERS, CONF_MOUSE) from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import CONF_HYDROMETERS, CONF_MOUSE, CONF_THERMOMETERS, HUB as hub + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/verisure/switch.py b/homeassistant/components/verisure/switch.py index a418eec6bc5577..eb69d4c02a18e4 100644 --- a/homeassistant/components/verisure/switch.py +++ b/homeassistant/components/verisure/switch.py @@ -2,10 +2,10 @@ import logging from time import time -from homeassistant.components.verisure import HUB as hub -from homeassistant.components.verisure import CONF_SMARTPLUGS from homeassistant.components.switch import SwitchDevice +from . import CONF_SMARTPLUGS, HUB as hub + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/volvooncall/binary_sensor.py b/homeassistant/components/volvooncall/binary_sensor.py index 7158e4df69b0d4..4b4f4ff77cc439 100644 --- a/homeassistant/components/volvooncall/binary_sensor.py +++ b/homeassistant/components/volvooncall/binary_sensor.py @@ -1,9 +1,10 @@ """Support for VOC.""" import logging -from homeassistant.components.volvooncall import VolvoEntity, DATA_KEY from homeassistant.components.binary_sensor import ( - BinarySensorDevice, DEVICE_CLASSES) + DEVICE_CLASSES, BinarySensorDevice) + +from . import DATA_KEY, VolvoEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/volvooncall/device_tracker.py b/homeassistant/components/volvooncall/device_tracker.py index d4838c0150583d..6c7e0914f6ef6a 100644 --- a/homeassistant/components/volvooncall/device_tracker.py +++ b/homeassistant/components/volvooncall/device_tracker.py @@ -1,10 +1,11 @@ """Support for tracking a Volvo.""" import logging -from homeassistant.util import slugify -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.device_tracker import SOURCE_TYPE_GPS -from homeassistant.components.volvooncall import DATA_KEY, SIGNAL_STATE_UPDATED +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.util import slugify + +from . import DATA_KEY, SIGNAL_STATE_UPDATED _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/volvooncall/lock.py b/homeassistant/components/volvooncall/lock.py index f281ea644619c8..4a81f9017ff523 100644 --- a/homeassistant/components/volvooncall/lock.py +++ b/homeassistant/components/volvooncall/lock.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.lock import LockDevice -from homeassistant.components.volvooncall import VolvoEntity, DATA_KEY + +from . import DATA_KEY, VolvoEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/volvooncall/sensor.py b/homeassistant/components/volvooncall/sensor.py index 07f16e580bd83b..8921bf057c12af 100644 --- a/homeassistant/components/volvooncall/sensor.py +++ b/homeassistant/components/volvooncall/sensor.py @@ -1,7 +1,7 @@ """Support for Volvo On Call sensors.""" import logging -from homeassistant.components.volvooncall import VolvoEntity, DATA_KEY +from . import DATA_KEY, VolvoEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/volvooncall/switch.py b/homeassistant/components/volvooncall/switch.py index d3985557cffb39..372909d1e0ac63 100644 --- a/homeassistant/components/volvooncall/switch.py +++ b/homeassistant/components/volvooncall/switch.py @@ -1,9 +1,10 @@ """Support for Volvo heater.""" import logging -from homeassistant.components.volvooncall import VolvoEntity, DATA_KEY from homeassistant.helpers.entity import ToggleEntity +from . import DATA_KEY, VolvoEntity + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vultr/binary_sensor.py b/homeassistant/components/vultr/binary_sensor.py index 149a6c282901ee..dccb648c9c2242 100644 --- a/homeassistant/components/vultr/binary_sensor.py +++ b/homeassistant/components/vultr/binary_sensor.py @@ -8,15 +8,16 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_NAME from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.vultr import ( - CONF_SUBSCRIPTION, ATTR_AUTO_BACKUPS, ATTR_ALLOWED_BANDWIDTH, - ATTR_CREATED_AT, ATTR_SUBSCRIPTION_ID, ATTR_SUBSCRIPTION_NAME, - ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_DISK, - ATTR_COST_PER_MONTH, ATTR_OS, ATTR_REGION, ATTR_VCPUS, DATA_VULTR) + PLATFORM_SCHEMA, BinarySensorDevice) +from homeassistant.const import CONF_NAME +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_ALLOWED_BANDWIDTH, ATTR_AUTO_BACKUPS, ATTR_COST_PER_MONTH, + ATTR_CREATED_AT, ATTR_DISK, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, + ATTR_MEMORY, ATTR_OS, ATTR_REGION, ATTR_SUBSCRIPTION_ID, + ATTR_SUBSCRIPTION_NAME, ATTR_VCPUS, CONF_SUBSCRIPTION, DATA_VULTR) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vultr/sensor.py b/homeassistant/components/vultr/sensor.py index a727e5bd2ecd5d..7ca731cabac74c 100644 --- a/homeassistant/components/vultr/sensor.py +++ b/homeassistant/components/vultr/sensor.py @@ -9,13 +9,14 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.vultr import ( - ATTR_CURRENT_BANDWIDTH_USED, ATTR_PENDING_CHARGES, CONF_SUBSCRIPTION, - DATA_VULTR) from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import ( + ATTR_CURRENT_BANDWIDTH_USED, ATTR_PENDING_CHARGES, CONF_SUBSCRIPTION, + DATA_VULTR) + _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Vultr {} {}' diff --git a/homeassistant/components/vultr/switch.py b/homeassistant/components/vultr/switch.py index 874d1979d7dc59..1f7d9ceaa28d23 100644 --- a/homeassistant/components/vultr/switch.py +++ b/homeassistant/components/vultr/switch.py @@ -8,14 +8,15 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice from homeassistant.const import CONF_NAME -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.components.vultr import ( - CONF_SUBSCRIPTION, ATTR_AUTO_BACKUPS, ATTR_ALLOWED_BANDWIDTH, - ATTR_CREATED_AT, ATTR_SUBSCRIPTION_ID, ATTR_SUBSCRIPTION_NAME, - ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_DISK, - ATTR_COST_PER_MONTH, ATTR_OS, ATTR_REGION, ATTR_VCPUS, DATA_VULTR) +import homeassistant.helpers.config_validation as cv + +from . import ( + ATTR_ALLOWED_BANDWIDTH, ATTR_AUTO_BACKUPS, ATTR_COST_PER_MONTH, + ATTR_CREATED_AT, ATTR_DISK, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, + ATTR_MEMORY, ATTR_OS, ATTR_REGION, ATTR_SUBSCRIPTION_ID, + ATTR_SUBSCRIPTION_NAME, ATTR_VCPUS, CONF_SUBSCRIPTION, DATA_VULTR) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/w800rf32/binary_sensor.py b/homeassistant/components/w800rf32/binary_sensor.py index 855a5f3ed0a8fd..c942483495306d 100644 --- a/homeassistant/components/w800rf32/binary_sensor.py +++ b/homeassistant/components/w800rf32/binary_sensor.py @@ -5,13 +5,13 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice) -from homeassistant.components.w800rf32 import (W800RF32_DEVICE) -from homeassistant.const import (CONF_DEVICE_CLASS, CONF_NAME, CONF_DEVICES) +from homeassistant.const import CONF_DEVICE_CLASS, CONF_DEVICES, CONF_NAME from homeassistant.core import callback -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers import event as evt +from homeassistant.helpers import config_validation as cv, event as evt +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import dt as dt_util -from homeassistant.helpers.dispatcher import (async_dispatcher_connect) + +from . import W800RF32_DEVICE _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/water_heater/demo.py b/homeassistant/components/water_heater/demo.py index b551993aca5d80..37ae535cdfc824 100644 --- a/homeassistant/components/water_heater/demo.py +++ b/homeassistant/components/water_heater/demo.py @@ -1,10 +1,9 @@ """Demo platform that offers a fake water heater device.""" -from homeassistant.components.water_heater import ( - WaterHeaterDevice, - SUPPORT_TARGET_TEMPERATURE, - SUPPORT_AWAY_MODE, - SUPPORT_OPERATION_MODE) -from homeassistant.const import TEMP_FAHRENHEIT, ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT + +from . import ( + SUPPORT_AWAY_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, + WaterHeaterDevice) SUPPORT_FLAGS_HEATER = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE | SUPPORT_AWAY_MODE) diff --git a/homeassistant/components/waterfurnace/sensor.py b/homeassistant/components/waterfurnace/sensor.py index 65632f51494e69..8a43a7dac77e2c 100644 --- a/homeassistant/components/waterfurnace/sensor.py +++ b/homeassistant/components/waterfurnace/sensor.py @@ -6,14 +6,13 @@ """ from homeassistant.components.sensor import ENTITY_ID_FORMAT -from homeassistant.components.waterfurnace import ( - DOMAIN as WF_DOMAIN, UPDATE_TOPIC -) from homeassistant.const import TEMP_FAHRENHEIT from homeassistant.core import callback from homeassistant.helpers.entity import Entity from homeassistant.util import slugify +from . import DOMAIN as WF_DOMAIN, UPDATE_TOPIC + class WFSensorConfig: """Water Furnace Sensor configuration.""" diff --git a/homeassistant/components/wink/alarm_control_panel.py b/homeassistant/components/wink/alarm_control_panel.py index 594198ddd121a0..73ca9a3cac4ac0 100644 --- a/homeassistant/components/wink/alarm_control_panel.py +++ b/homeassistant/components/wink/alarm_control_panel.py @@ -2,10 +2,11 @@ import logging import homeassistant.components.alarm_control_panel as alarm -from homeassistant.components.wink import DOMAIN, WinkDevice from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) +from . import DOMAIN, WinkDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['wink'] diff --git a/homeassistant/components/wink/binary_sensor.py b/homeassistant/components/wink/binary_sensor.py index 12e1b557a733da..f3757d7bf39624 100644 --- a/homeassistant/components/wink/binary_sensor.py +++ b/homeassistant/components/wink/binary_sensor.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.wink import DOMAIN, WinkDevice + +from . import DOMAIN, WinkDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wink/climate.py b/homeassistant/components/wink/climate.py index efd8eecf5afcd4..f5e75c1fb8d591 100644 --- a/homeassistant/components/wink/climate.py +++ b/homeassistant/components/wink/climate.py @@ -4,17 +4,17 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( ATTR_CURRENT_HUMIDITY, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - STATE_AUTO, STATE_COOL, STATE_ECO, - STATE_FAN_ONLY, STATE_HEAT, SUPPORT_AUX_HEAT, - SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, + STATE_AUTO, STATE_COOL, STATE_ECO, STATE_FAN_ONLY, STATE_HEAT, + SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) -from homeassistant.components.wink import DOMAIN, WinkDevice from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, STATE_OFF, STATE_ON, STATE_UNKNOWN, TEMP_CELSIUS) from homeassistant.helpers.temperature import display_temp as show_temp +from . import DOMAIN, WinkDevice + _LOGGER = logging.getLogger(__name__) ATTR_ECO_TARGET = 'eco_target' diff --git a/homeassistant/components/wink/cover.py b/homeassistant/components/wink/cover.py index 19ff792592fee4..f4c4841c2a2d0e 100644 --- a/homeassistant/components/wink/cover.py +++ b/homeassistant/components/wink/cover.py @@ -1,6 +1,7 @@ """Support for Wink covers.""" -from homeassistant.components.cover import CoverDevice, ATTR_POSITION -from homeassistant.components.wink import WinkDevice, DOMAIN +from homeassistant.components.cover import ATTR_POSITION, CoverDevice + +from . import DOMAIN, WinkDevice DEPENDENCIES = ['wink'] diff --git a/homeassistant/components/wink/fan.py b/homeassistant/components/wink/fan.py index 5bc6e03c49b964..52a27eb3c3df19 100644 --- a/homeassistant/components/wink/fan.py +++ b/homeassistant/components/wink/fan.py @@ -2,9 +2,10 @@ import logging from homeassistant.components.fan import ( - SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SUPPORT_DIRECTION, - SUPPORT_SET_SPEED, FanEntity) -from homeassistant.components.wink import DOMAIN, WinkDevice + SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SUPPORT_DIRECTION, SUPPORT_SET_SPEED, + FanEntity) + +from . import DOMAIN, WinkDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wink/light.py b/homeassistant/components/wink/light.py index 14a983154f82f5..95747bcc1b2c63 100644 --- a/homeassistant/components/wink/light.py +++ b/homeassistant/components/wink/light.py @@ -1,11 +1,12 @@ """Support for Wink lights.""" from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, - SUPPORT_COLOR_TEMP, SUPPORT_COLOR, Light) -from homeassistant.components.wink import DOMAIN, WinkDevice + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) from homeassistant.util import color as color_util -from homeassistant.util.color import \ - color_temperature_mired_to_kelvin as mired_to_kelvin +from homeassistant.util.color import ( + color_temperature_mired_to_kelvin as mired_to_kelvin) + +from . import DOMAIN, WinkDevice DEPENDENCIES = ['wink'] diff --git a/homeassistant/components/wink/lock.py b/homeassistant/components/wink/lock.py index 0ef4f30dc5a666..8e6fb9b280530e 100644 --- a/homeassistant/components/wink/lock.py +++ b/homeassistant/components/wink/lock.py @@ -4,11 +4,12 @@ import voluptuous as vol from homeassistant.components.lock import LockDevice -from homeassistant.components.wink import DOMAIN, WinkDevice from homeassistant.const import ( ATTR_CODE, ATTR_ENTITY_ID, ATTR_NAME, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv +from . import DOMAIN, WinkDevice + DEPENDENCIES = ['wink'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wink/scene.py b/homeassistant/components/wink/scene.py index d05fe58a4271c8..e77402c4d45880 100644 --- a/homeassistant/components/wink/scene.py +++ b/homeassistant/components/wink/scene.py @@ -2,7 +2,8 @@ import logging from homeassistant.components.scene import Scene -from homeassistant.components.wink import DOMAIN, WinkDevice + +from . import DOMAIN, WinkDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wink/sensor.py b/homeassistant/components/wink/sensor.py index ab61769c94d897..3dfd704d564a36 100644 --- a/homeassistant/components/wink/sensor.py +++ b/homeassistant/components/wink/sensor.py @@ -1,9 +1,10 @@ """Support for Wink sensors.""" import logging -from homeassistant.components.wink import DOMAIN, WinkDevice from homeassistant.const import TEMP_CELSIUS +from . import DOMAIN, WinkDevice + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['wink'] diff --git a/homeassistant/components/wink/switch.py b/homeassistant/components/wink/switch.py index cd55026879ade9..6ee777dd1fcccd 100644 --- a/homeassistant/components/wink/switch.py +++ b/homeassistant/components/wink/switch.py @@ -1,9 +1,10 @@ """Support for Wink switches.""" import logging -from homeassistant.components.wink import DOMAIN, WinkDevice from homeassistant.helpers.entity import ToggleEntity +from . import DOMAIN, WinkDevice + DEPENDENCIES = ['wink'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wink/water_heater.py b/homeassistant/components/wink/water_heater.py index 34cd86a50f4e13..343f4a766014f2 100644 --- a/homeassistant/components/wink/water_heater.py +++ b/homeassistant/components/wink/water_heater.py @@ -2,14 +2,12 @@ import logging from homeassistant.components.water_heater import ( - ATTR_TEMPERATURE, STATE_ECO, STATE_ELECTRIC, - STATE_PERFORMANCE, SUPPORT_AWAY_MODE, STATE_HEAT_PUMP, - STATE_GAS, STATE_HIGH_DEMAND, - SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, - WaterHeaterDevice) -from homeassistant.components.wink import DOMAIN, WinkDevice -from homeassistant.const import ( - STATE_OFF, STATE_UNKNOWN, TEMP_CELSIUS) + ATTR_TEMPERATURE, STATE_ECO, STATE_ELECTRIC, STATE_GAS, STATE_HEAT_PUMP, + STATE_HIGH_DEMAND, STATE_PERFORMANCE, SUPPORT_AWAY_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, WaterHeaterDevice) +from homeassistant.const import STATE_OFF, STATE_UNKNOWN, TEMP_CELSIUS + +from . import DOMAIN, WinkDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wirelesstag/binary_sensor.py b/homeassistant/components/wirelesstag/binary_sensor.py index 6f2c24a7788d74..aefa5ed34a9dad 100644 --- a/homeassistant/components/wirelesstag/binary_sensor.py +++ b/homeassistant/components/wirelesstag/binary_sensor.py @@ -3,17 +3,16 @@ import voluptuous as vol +from homeassistant.components.binary_sensor import ( + PLATFORM_SCHEMA, BinarySensorDevice) +from homeassistant.const import CONF_MONITORED_CONDITIONS, STATE_OFF, STATE_ON from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.components.binary_sensor import ( - BinarySensorDevice, PLATFORM_SCHEMA) -from homeassistant.components.wirelesstag import ( - DOMAIN as WIRELESSTAG_DOMAIN, - SIGNAL_BINARY_EVENT_UPDATE, + +from . import ( + DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_BINARY_EVENT_UPDATE, WirelessTagBaseSensor) -from homeassistant.const import ( - CONF_MONITORED_CONDITIONS, STATE_ON, STATE_OFF) -import homeassistant.helpers.config_validation as cv DEPENDENCIES = ['wirelesstag'] diff --git a/homeassistant/components/wirelesstag/sensor.py b/homeassistant/components/wirelesstag/sensor.py index 3703e214d835a3..ca26e07b985ae9 100644 --- a/homeassistant/components/wirelesstag/sensor.py +++ b/homeassistant/components/wirelesstag/sensor.py @@ -1,17 +1,16 @@ """Sensor support for Wireless Sensor Tags platform.""" import logging + import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.const import ( - CONF_MONITORED_CONDITIONS) -from homeassistant.components.wirelesstag import ( - DOMAIN as WIRELESSTAG_DOMAIN, - SIGNAL_TAG_UPDATE, - WirelessTagBaseSensor) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( + DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_TAG_UPDATE, WirelessTagBaseSensor) DEPENDENCIES = ['wirelesstag'] diff --git a/homeassistant/components/wirelesstag/switch.py b/homeassistant/components/wirelesstag/switch.py index 913438e9d8c86b..4a2b64acda1d75 100644 --- a/homeassistant/components/wirelesstag/switch.py +++ b/homeassistant/components/wirelesstag/switch.py @@ -3,15 +3,12 @@ import voluptuous as vol - -from homeassistant.components.wirelesstag import ( - DOMAIN as WIRELESSTAG_DOMAIN, - WirelessTagBaseSensor) from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice -from homeassistant.const import ( - CONF_MONITORED_CONDITIONS) +from homeassistant.const import CONF_MONITORED_CONDITIONS import homeassistant.helpers.config_validation as cv +from . import DOMAIN as WIRELESSTAG_DOMAIN, WirelessTagBaseSensor + DEPENDENCIES = ['wirelesstag'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/binary_sensor.py b/homeassistant/components/xiaomi_aqara/binary_sensor.py index afffb71bae5fdc..7eb72e91eef714 100644 --- a/homeassistant/components/xiaomi_aqara/binary_sensor.py +++ b/homeassistant/components/xiaomi_aqara/binary_sensor.py @@ -2,11 +2,11 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) from homeassistant.core import callback from homeassistant.helpers.event import async_call_later +from . import PY_XIAOMI_GATEWAY, XiaomiDevice + _LOGGER = logging.getLogger(__name__) NO_CLOSE = 'no_close' diff --git a/homeassistant/components/xiaomi_aqara/cover.py b/homeassistant/components/xiaomi_aqara/cover.py index ead2c0e9219fdc..f4bf1f269b5cfc 100644 --- a/homeassistant/components/xiaomi_aqara/cover.py +++ b/homeassistant/components/xiaomi_aqara/cover.py @@ -1,9 +1,9 @@ """Support for Xiaomi curtain.""" import logging -from homeassistant.components.cover import CoverDevice, ATTR_POSITION -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) +from homeassistant.components.cover import ATTR_POSITION, CoverDevice + +from . import PY_XIAOMI_GATEWAY, XiaomiDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/light.py b/homeassistant/components/xiaomi_aqara/light.py index 30433ccea3d67b..d0fbdea8fad97a 100644 --- a/homeassistant/components/xiaomi_aqara/light.py +++ b/homeassistant/components/xiaomi_aqara/light.py @@ -1,14 +1,14 @@ """Support for Xiaomi Gateway Light.""" +import binascii import logging import struct -import binascii -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) -from homeassistant.components.light import (ATTR_BRIGHTNESS, ATTR_HS_COLOR, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, Light) + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, Light) import homeassistant.util.color as color_util +from . import PY_XIAOMI_GATEWAY, XiaomiDevice + _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/lock.py b/homeassistant/components/xiaomi_aqara/lock.py index f19492664b1a04..56d68f38be94ee 100644 --- a/homeassistant/components/xiaomi_aqara/lock.py +++ b/homeassistant/components/xiaomi_aqara/lock.py @@ -1,11 +1,12 @@ """Support for Xiaomi Aqara locks.""" import logging -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) + from homeassistant.components.lock import LockDevice -from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED) -from homeassistant.helpers.event import async_call_later +from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED from homeassistant.core import callback +from homeassistant.helpers.event import async_call_later + +from . import PY_XIAOMI_GATEWAY, XiaomiDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/sensor.py b/homeassistant/components/xiaomi_aqara/sensor.py index 133814e216eeff..c5cc00f14ba5f0 100644 --- a/homeassistant/components/xiaomi_aqara/sensor.py +++ b/homeassistant/components/xiaomi_aqara/sensor.py @@ -1,11 +1,11 @@ """Support for Xiaomi Aqara sensors.""" import logging -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) from homeassistant.const import ( - DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, - TEMP_CELSIUS, DEVICE_CLASS_PRESSURE) + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS) + +from . import PY_XIAOMI_GATEWAY, XiaomiDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_aqara/switch.py b/homeassistant/components/xiaomi_aqara/switch.py index c3cde8ede6d1d0..211f9d127c4fa3 100644 --- a/homeassistant/components/xiaomi_aqara/switch.py +++ b/homeassistant/components/xiaomi_aqara/switch.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.switch import SwitchDevice -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) + +from . import PY_XIAOMI_GATEWAY, XiaomiDevice _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xs1/climate.py b/homeassistant/components/xs1/climate.py index e579761474bbbd..080b87c13469a2 100644 --- a/homeassistant/components/xs1/climate.py +++ b/homeassistant/components/xs1/climate.py @@ -3,12 +3,11 @@ import logging from homeassistant.components.climate import ClimateDevice -from homeassistant.components.climate.const import ( - SUPPORT_TARGET_TEMPERATURE) -from homeassistant.components.xs1 import ( - ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity) +from homeassistant.components.climate.const import SUPPORT_TARGET_TEMPERATURE from homeassistant.const import ATTR_TEMPERATURE +from . import ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity + DEPENDENCIES = ['xs1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xs1/sensor.py b/homeassistant/components/xs1/sensor.py index 9de91a6b0123ca..f5fdcf1fb34503 100644 --- a/homeassistant/components/xs1/sensor.py +++ b/homeassistant/components/xs1/sensor.py @@ -1,10 +1,10 @@ """Support for XS1 sensors.""" import logging -from homeassistant.components.xs1 import ( - ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity) from homeassistant.helpers.entity import Entity +from . import ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity + DEPENDENCIES = ['xs1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xs1/switch.py b/homeassistant/components/xs1/switch.py index 35568587b1969b..d8b344fc716a39 100644 --- a/homeassistant/components/xs1/switch.py +++ b/homeassistant/components/xs1/switch.py @@ -1,10 +1,10 @@ """Support for XS1 switches.""" import logging -from homeassistant.components.xs1 import ( - ACTUATORS, DOMAIN as COMPONENT_DOMAIN, XS1DeviceEntity) from homeassistant.helpers.entity import ToggleEntity +from . import ACTUATORS, DOMAIN as COMPONENT_DOMAIN, XS1DeviceEntity + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['xs1'] diff --git a/homeassistant/components/zamg/weather.py b/homeassistant/components/zamg/weather.py index 01a927afbf8bbf..bc90a56cc10944 100644 --- a/homeassistant/components/zamg/weather.py +++ b/homeassistant/components/zamg/weather.py @@ -3,9 +3,6 @@ import voluptuous as vol -# Reuse data and API logic from the sensor implementation -from homeassistant.components.zamg.sensor import ( - ATTRIBUTION, CONF_STATION_ID, ZamgData, closest_station, zamg_stations) from homeassistant.components.weather import ( ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_WIND_BEARING, ATTR_WEATHER_WIND_SPEED, PLATFORM_SCHEMA, @@ -14,6 +11,10 @@ CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers import config_validation as cv +# Reuse data and API logic from the sensor implementation +from .sensor import ( + ATTRIBUTION, CONF_STATION_ID, ZamgData, closest_station, zamg_stations) + _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/zigbee/binary_sensor.py b/homeassistant/components/zigbee/binary_sensor.py index eec1832f07d901..ccf4e70df34a18 100644 --- a/homeassistant/components/zigbee/binary_sensor.py +++ b/homeassistant/components/zigbee/binary_sensor.py @@ -2,8 +2,8 @@ import voluptuous as vol from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.zigbee import ( - ZigBeeDigitalIn, ZigBeeDigitalInConfig, PLATFORM_SCHEMA) + +from . import PLATFORM_SCHEMA, ZigBeeDigitalIn, ZigBeeDigitalInConfig CONF_ON_STATE = 'on_state' diff --git a/homeassistant/components/zigbee/light.py b/homeassistant/components/zigbee/light.py index e5016900be7012..b9be0d893239d0 100644 --- a/homeassistant/components/zigbee/light.py +++ b/homeassistant/components/zigbee/light.py @@ -2,8 +2,8 @@ import voluptuous as vol from homeassistant.components.light import Light -from homeassistant.components.zigbee import ( - ZigBeeDigitalOut, ZigBeeDigitalOutConfig, PLATFORM_SCHEMA) + +from . import PLATFORM_SCHEMA, ZigBeeDigitalOut, ZigBeeDigitalOutConfig CONF_ON_STATE = 'on_state' diff --git a/homeassistant/components/zigbee/sensor.py b/homeassistant/components/zigbee/sensor.py index 48503e396a4b49..48301ac9728ee5 100644 --- a/homeassistant/components/zigbee/sensor.py +++ b/homeassistant/components/zigbee/sensor.py @@ -1,14 +1,15 @@ """Support for Zigbee sensors.""" -import logging from binascii import hexlify +import logging import voluptuous as vol from homeassistant.components import zigbee -from homeassistant.components.zigbee import PLATFORM_SCHEMA from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity +from . import PLATFORM_SCHEMA + _LOGGER = logging.getLogger(__name__) CONF_TYPE = 'type' diff --git a/homeassistant/components/zigbee/switch.py b/homeassistant/components/zigbee/switch.py index ef36e17d74b48c..ddfd47a047e9c6 100644 --- a/homeassistant/components/zigbee/switch.py +++ b/homeassistant/components/zigbee/switch.py @@ -2,8 +2,8 @@ import voluptuous as vol from homeassistant.components.switch import SwitchDevice -from homeassistant.components.zigbee import ( - ZigBeeDigitalOut, ZigBeeDigitalOutConfig, PLATFORM_SCHEMA) + +from . import PLATFORM_SCHEMA, ZigBeeDigitalOut, ZigBeeDigitalOutConfig DEPENDENCIES = ['zigbee'] diff --git a/homeassistant/components/zoneminder/binary_sensor.py b/homeassistant/components/zoneminder/binary_sensor.py index f20f09e70a6ee4..ce59d4573bea1d 100644 --- a/homeassistant/components/zoneminder/binary_sensor.py +++ b/homeassistant/components/zoneminder/binary_sensor.py @@ -1,7 +1,7 @@ """Support for ZoneMinder binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.zoneminder import DOMAIN as ZONEMINDER_DOMAIN +from . import DOMAIN as ZONEMINDER_DOMAIN DEPENDENCIES = ['zoneminder'] diff --git a/homeassistant/components/zoneminder/camera.py b/homeassistant/components/zoneminder/camera.py index f9dd8718b8f89e..fe3333fa3ed274 100644 --- a/homeassistant/components/zoneminder/camera.py +++ b/homeassistant/components/zoneminder/camera.py @@ -1,10 +1,11 @@ """Support for ZoneMinder camera streaming.""" import logging -from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL from homeassistant.components.mjpeg.camera import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera, filter_urllib3_logging) -from homeassistant.components.zoneminder import DOMAIN as ZONEMINDER_DOMAIN +from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL + +from . import DOMAIN as ZONEMINDER_DOMAIN _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zoneminder/sensor.py b/homeassistant/components/zoneminder/sensor.py index 9eb6beb491c77c..e205d921422be2 100644 --- a/homeassistant/components/zoneminder/sensor.py +++ b/homeassistant/components/zoneminder/sensor.py @@ -3,12 +3,13 @@ import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.components.zoneminder import DOMAIN as ZONEMINDER_DOMAIN from homeassistant.const import CONF_MONITORED_CONDITIONS +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity +from . import DOMAIN as ZONEMINDER_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['zoneminder'] diff --git a/homeassistant/components/zoneminder/switch.py b/homeassistant/components/zoneminder/switch.py index b411a148d43fff..78e72c5fd4a88f 100644 --- a/homeassistant/components/zoneminder/switch.py +++ b/homeassistant/components/zoneminder/switch.py @@ -3,11 +3,12 @@ import voluptuous as vol -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.components.zoneminder import DOMAIN as ZONEMINDER_DOMAIN -from homeassistant.const import (CONF_COMMAND_ON, CONF_COMMAND_OFF) +from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice +from homeassistant.const import CONF_COMMAND_OFF, CONF_COMMAND_ON import homeassistant.helpers.config_validation as cv +from . import DOMAIN as ZONEMINDER_DOMAIN + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['zoneminder'] From 88be786e82106297e091191b6fe4247b231419a4 Mon Sep 17 00:00:00 2001 From: Quentin Stafford-Fraser Date: Thu, 21 Mar 2019 06:10:09 +0000 Subject: [PATCH 060/605] Make !include_dir_list use alphanumeric order (#21902) * Make YAML includes such as !include_dir_list incorporate files in alphabetical order * Test for !include_dir_list sorting --- homeassistant/util/yaml.py | 2 +- tests/util/test_yaml.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/util/yaml.py b/homeassistant/util/yaml.py index c988cb811b2c96..15bf73f459d061 100644 --- a/homeassistant/util/yaml.py +++ b/homeassistant/util/yaml.py @@ -144,7 +144,7 @@ def _find_files(directory: str, pattern: str) -> Iterator[str]: """Recursively load files in a directory.""" for root, dirs, files in os.walk(directory, topdown=True): dirs[:] = [d for d in dirs if _is_file_valid(d)] - for basename in files: + for basename in sorted(files): if _is_file_valid(basename) and fnmatch.fnmatch(basename, pattern): filename = os.path.join(root, basename) yield filename diff --git a/tests/util/test_yaml.py b/tests/util/test_yaml.py index 2eab75cb92acc3..46dc3c045b2507 100644 --- a/tests/util/test_yaml.py +++ b/tests/util/test_yaml.py @@ -95,7 +95,7 @@ def test_include_yaml(self): def test_include_dir_list(self, mock_walk): """Test include dir list yaml.""" mock_walk.return_value = [ - ['/tmp', [], ['one.yaml', 'two.yaml']], + ['/tmp', [], ['two.yaml', 'one.yaml']], ] with patch_yaml_files({ @@ -105,7 +105,7 @@ def test_include_dir_list(self, mock_walk): conf = "key: !include_dir_list /tmp" with io.StringIO(conf) as file: doc = yaml.yaml.safe_load(file) - assert sorted(doc["key"]) == sorted(["one", "two"]) + assert doc["key"] == sorted(["one", "two"]) @patch('homeassistant.util.yaml.os.walk') def test_include_dir_list_recursive(self, mock_walk): From 21871b3d6b10fd0a3365970c82dba69543d1f8b8 Mon Sep 17 00:00:00 2001 From: uchagani Date: Thu, 21 Mar 2019 03:55:30 -0400 Subject: [PATCH 061/605] add date_time_iso to time_date sensor (#22199) * add date_time_iso to time_date sensor * hound fixes * lint fixes --- homeassistant/components/time_date/sensor.py | 4 ++++ tests/components/time_date/test_sensor.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/homeassistant/components/time_date/sensor.py b/homeassistant/components/time_date/sensor.py index 1b346d409c4197..7825867df64fa4 100644 --- a/homeassistant/components/time_date/sensor.py +++ b/homeassistant/components/time_date/sensor.py @@ -25,6 +25,7 @@ 'time': 'Time', 'date': 'Date', 'date_time': 'Date & Time', + 'date_time_iso': 'Date & Time ISO', 'time_date': 'Time & Date', 'beat': 'Internet Time', 'time_utc': 'Time (UTC)', @@ -123,6 +124,9 @@ def _update_internal_state(self, time_date): self._state = time_utc elif self.type == 'beat': self._state = '@{0:03d}'.format(beat) + elif self.type == 'date_time_iso': + self._state = dt_util.parse_datetime( + '{} {}'.format(date, time)).isoformat() @callback def point_in_time_listener(self, time_date): diff --git a/tests/components/time_date/test_sensor.py b/tests/components/time_date/test_sensor.py index b2a22439c1f547..84331abfba1b49 100644 --- a/tests/components/time_date/test_sensor.py +++ b/tests/components/time_date/test_sensor.py @@ -71,6 +71,10 @@ def test_states(self): device._update_internal_state(now) assert device.state == "@079" + device = time_date.TimeDateSensor(self.hass, 'date_time_iso') + device._update_internal_state(now) + assert device.state == "2017-05-18T00:54:00" + # pylint: disable=no-member def test_timezone_intervals(self): """Test date sensor behavior in a timezone besides UTC.""" @@ -114,3 +118,5 @@ def test_icons(self): assert device.icon == "mdi:calendar" device = time_date.TimeDateSensor(self.hass, 'date_time') assert device.icon == "mdi:calendar-clock" + device = time_date.TimeDateSensor(self.hass, 'date_time_iso') + assert device.icon == "mdi:calendar-clock" From f4102339c175586f57345dcc389ebea21cb9fe83 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Thu, 21 Mar 2019 08:56:36 +0100 Subject: [PATCH 062/605] Bump python-miio version (#22202) * Bump python-miio version * Rename speed property to motor_speed * Enable set_led service of the Air Humidifier * Allow a favorite level in [0...17] * Allow a scene in [0...6] --- homeassistant/components/xiaomi_miio/device_tracker.py | 2 +- homeassistant/components/xiaomi_miio/fan.py | 7 ++++--- homeassistant/components/xiaomi_miio/light.py | 4 ++-- homeassistant/components/xiaomi_miio/remote.py | 2 +- homeassistant/components/xiaomi_miio/sensor.py | 2 +- homeassistant/components/xiaomi_miio/switch.py | 2 +- homeassistant/components/xiaomi_miio/vacuum.py | 2 +- requirements_all.txt | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/device_tracker.py b/homeassistant/components/xiaomi_miio/device_tracker.py index 1aec3647d617ee..e7ea9fbbb408d9 100644 --- a/homeassistant/components/xiaomi_miio/device_tracker.py +++ b/homeassistant/components/xiaomi_miio/device_tracker.py @@ -8,7 +8,7 @@ from homeassistant.const import CONF_HOST, CONF_TOKEN import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index c4cfa6bbe6b768..51d4780160dacf 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -13,7 +13,7 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) @@ -227,7 +227,7 @@ AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_CA = { **AVAILABLE_ATTRIBUTES_AIRHUMIDIFIER_COMMON, - ATTR_MOTOR_SPEED: 'speed', + ATTR_MOTOR_SPEED: 'motor_speed', ATTR_DEPTH: 'depth', ATTR_DRY: 'dry', } @@ -305,6 +305,7 @@ FEATURE_FLAGS_AIRHUMIDIFIER = (FEATURE_SET_BUZZER | FEATURE_SET_CHILD_LOCK | + FEATURE_SET_LED | FEATURE_SET_LED_BRIGHTNESS | FEATURE_SET_TARGET_HUMIDITY) @@ -348,7 +349,7 @@ SERVICE_SCHEMA_FAVORITE_LEVEL = AIRPURIFIER_SERVICE_SCHEMA.extend({ vol.Required(ATTR_LEVEL): - vol.All(vol.Coerce(int), vol.Clamp(min=0, max=16)) + vol.All(vol.Coerce(int), vol.Clamp(min=0, max=17)) }) SERVICE_SCHEMA_VOLUME = AIRPURIFIER_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index 3ae6bfe6cd7fc0..ecf7b12e4ac1b8 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -16,7 +16,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import color, dt -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) @@ -81,7 +81,7 @@ SERVICE_SCHEMA_SET_SCENE = XIAOMI_MIIO_SERVICE_SCHEMA.extend({ vol.Required(ATTR_SCENE): - vol.All(vol.Coerce(int), vol.Clamp(min=1, max=4)) + vol.All(vol.Coerce(int), vol.Clamp(min=1, max=6)) }) SERVICE_SCHEMA_SET_DELAYED_TURN_OFF = XIAOMI_MIIO_SERVICE_SCHEMA.extend({ diff --git a/homeassistant/components/xiaomi_miio/remote.py b/homeassistant/components/xiaomi_miio/remote.py index 1c4ae85a0d8d9e..450279c18253e2 100644 --- a/homeassistant/components/xiaomi_miio/remote.py +++ b/homeassistant/components/xiaomi_miio/remote.py @@ -17,7 +17,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.dt import utcnow -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index bd5f8642e5434a..41d3ce65b13d84 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -9,7 +9,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index eb7f09f95e679d..f330030922d0a2 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -12,7 +12,7 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_miio/vacuum.py b/homeassistant/components/xiaomi_miio/vacuum.py index 361961586008b3..c7f69a4033030a 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -16,7 +16,7 @@ ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45'] +REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index ef5aa8f8cfc016..786e9b820223f5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1354,7 +1354,7 @@ python-juicenet==0.0.5 # homeassistant.components.xiaomi_miio.sensor # homeassistant.components.xiaomi_miio.switch # homeassistant.components.xiaomi_miio.vacuum -python-miio==0.4.4 +python-miio==0.4.5 # homeassistant.components.mpd.media_player python-mpd2==1.0.0 From 77635d40e2cd3e633204303f05bb20c909daf383 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Thu, 21 Mar 2019 08:58:04 +0100 Subject: [PATCH 063/605] Upgrade to async_upnp_client==0.14.6 (#22223) Upgrade to async_upnp_client to 0.14.6 --- homeassistant/components/dlna_dmr/media_player.py | 2 +- homeassistant/components/upnp/__init__.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index fbb0ee58c5afa1..aae2e9b6af9cfd 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -29,7 +29,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import get_local_ip -REQUIREMENTS = ['async-upnp-client==0.14.5'] +REQUIREMENTS = ['async-upnp-client==0.14.6'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/upnp/__init__.py b/homeassistant/components/upnp/__init__.py index a9fb84f733e258..ce72eff2ba89ab 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,7 +23,7 @@ from .const import LOGGER as _LOGGER from .device import Device -REQUIREMENTS = ['async-upnp-client==0.14.5'] +REQUIREMENTS = ['async-upnp-client==0.14.6'] NOTIFICATION_ID = 'upnp_notification' NOTIFICATION_TITLE = 'UPnP/IGD Setup' diff --git a/requirements_all.txt b/requirements_all.txt index 786e9b820223f5..3854fd2c00fe41 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -177,7 +177,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.upnp # homeassistant.components.dlna_dmr.media_player -async-upnp-client==0.14.5 +async-upnp-client==0.14.6 # homeassistant.components.stream av==6.1.2 From 07dc23a0e3a17b8dbc75d5aa13c4c5bf2a390b87 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Thu, 21 Mar 2019 10:31:55 -0400 Subject: [PATCH 064/605] Stream fixes (#22238) * fix issues with out of order packets, and empty first packet on some IP camera models * do not skip the first packet --- homeassistant/components/stream/worker.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 3a3e19d970330a..d0196761968b38 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -63,21 +63,29 @@ def stream_worker(hass, stream, quit_event): first_packet = True sequence = 1 audio_packets = {} + last_dts = None while not quit_event.is_set(): try: packet = next(container.demux(video_stream)) if packet.dts is None: + if first_packet: + continue # If we get a "flushing" packet, the stream is done - raise StopIteration + raise StopIteration("No dts in packet") except (av.AVError, StopIteration) as ex: # End of stream, clear listeners and stop thread for fmt, _ in outputs.items(): hass.loop.call_soon_threadsafe( stream.outputs[fmt].put, None) - _LOGGER.error("Error demuxing stream: %s", ex) + _LOGGER.error("Error demuxing stream: %s", str(ex)) break + # Skip non monotonically increasing dts in feed + if not first_packet and last_dts >= packet.dts: + continue + last_dts = packet.dts + # Reset segment on every keyframe if packet.is_keyframe: # Save segment to outputs From 2b02c0d0fca64d3f9f2c7cfd39fbd0b7977c3caa Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 21 Mar 2019 07:32:13 -0700 Subject: [PATCH 065/605] Fix build issue (#22251) --- homeassistant/components/buienradar/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index fa386a177057d3..d144d84cbf81e6 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -143,7 +143,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Create the buienradar sensor.""" - from ..weather import DEFAULT_TIMEFRAME + from .weather import DEFAULT_TIMEFRAME latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) From 03855c18fc161ce6231fe6723b190042e8584791 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 21 Mar 2019 10:40:12 -0400 Subject: [PATCH 066/605] add ZHA channel name property (#22218) * Make channel name a property. * Cleanup Zigbee channels. Use zcl.Cluster.ep_attribute as default channel name. --- .../components/zha/core/channels/__init__.py | 25 ++++++++++++++----- .../components/zha/core/channels/general.py | 15 +---------- .../zha/core/channels/homeautomation.py | 12 +++------ .../components/zha/core/channels/hvac.py | 7 +----- .../components/zha/core/channels/lighting.py | 2 -- .../components/zha/core/channels/security.py | 7 +----- homeassistant/components/zha/core/const.py | 8 +++--- 7 files changed, 30 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index ef3ef71477fd3a..d8a3918889d9bd 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -83,9 +83,14 @@ class ChannelStatus(Enum): class ZigbeeChannel: """Base channel for a Zigbee cluster.""" + CHANNEL_NAME = None + def __init__(self, cluster, device): """Initialize ZigbeeChannel.""" - self.name = 'channel_{}'.format(cluster.cluster_id) + self._channel_name = cluster.ep_attribute + if self.CHANNEL_NAME: + self._channel_name = self.CHANNEL_NAME + self._generic_id = 'channel_0x{:04x}'.format(cluster.cluster_id) self._cluster = cluster self._zha_device = device self._unique_id = construct_unique_id(cluster) @@ -96,6 +101,11 @@ def __init__(self, cluster, device): self._status = ChannelStatus.CREATED self._cluster.add_listener(self) + @property + def generic_id(self): + """Return the generic id for this channel.""" + return self._generic_id + @property def unique_id(self): """Return the unique id for this channel.""" @@ -111,6 +121,11 @@ def device(self): """Return the device this channel is linked to.""" return self._zha_device + @property + def name(self) -> str: + """Return friendly name.""" + return self._channel_name + @property def status(self): """Return the status of the channel.""" @@ -215,10 +230,11 @@ def __getattr__(self, name): class AttributeListeningChannel(ZigbeeChannel): """Channel for attribute reports from the cluster.""" + CHANNEL_NAME = ATTRIBUTE_CHANNEL + def __init__(self, cluster, device): """Initialize AttributeListeningChannel.""" super().__init__(cluster, device) - self.name = ATTRIBUTE_CHANNEL attr = self._report_config[0].get('attr') if isinstance(attr, str): self.value_attribute = get_attr_id_by_name(self.cluster, attr) @@ -340,10 +356,7 @@ async def async_configure(self): class EventRelayChannel(ZigbeeChannel): """Event relay that can be attached to zigbee clusters.""" - def __init__(self, cluster, device): - """Initialize EventRelayChannel.""" - super().__init__(cluster, device) - self.name = EVENT_RELAY_CHANNEL + CHANNEL_NAME = EVENT_RELAY_CHANNEL @callback def attribute_updated(self, attrid, value): diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index c0b0367be99930..bf0f1044efb67c 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -11,8 +11,7 @@ from ..helpers import get_attr_id_by_name from ..const import ( SIGNAL_ATTR_UPDATED, SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, - SIGNAL_STATE_ATTR, BASIC_CHANNEL, ON_OFF_CHANNEL, LEVEL_CHANNEL, - POWER_CONFIGURATION_CHANNEL + SIGNAL_STATE_ATTR ) _LOGGER = logging.getLogger(__name__) @@ -26,7 +25,6 @@ class OnOffChannel(ZigbeeChannel): def __init__(self, cluster, device): """Initialize OnOffChannel.""" super().__init__(cluster, device) - self.name = ON_OFF_CHANNEL self._state = None @callback @@ -77,11 +75,6 @@ class LevelControlChannel(ZigbeeChannel): CURRENT_LEVEL = 0 - def __init__(self, cluster, device): - """Initialize LevelControlChannel.""" - super().__init__(cluster, device) - self.name = LEVEL_CHANNEL - @callback def cluster_command(self, tsn, command_id, args): """Handle commands received to this cluster.""" @@ -149,7 +142,6 @@ class BasicChannel(ZigbeeChannel): def __init__(self, cluster, device): """Initialize BasicChannel.""" super().__init__(cluster, device) - self.name = BASIC_CHANNEL self._power_source = None async def async_configure(self): @@ -171,11 +163,6 @@ def get_power_source(self): class PowerConfigurationChannel(ZigbeeChannel): """Channel for the zigbee power configuration cluster.""" - def __init__(self, cluster, device): - """Initialize PowerConfigurationChannel.""" - super().__init__(cluster, device) - self.name = POWER_CONFIGURATION_CHANNEL - @callback def attribute_updated(self, attrid, value): """Handle attribute updates on this cluster.""" diff --git a/homeassistant/components/zha/core/channels/homeautomation.py b/homeassistant/components/zha/core/channels/homeautomation.py index 2518889fcb14d7..e4b67dd0db75be 100644 --- a/homeassistant/components/zha/core/channels/homeautomation.py +++ b/homeassistant/components/zha/core/channels/homeautomation.py @@ -15,18 +15,15 @@ class ElectricalMeasurementChannel(AttributeListeningChannel): """Channel that polls active power level.""" - def __init__(self, cluster, device): - """Initialize ElectricalMeasurementChannel.""" - super().__init__(cluster, device) - self.name = ELECTRICAL_MEASUREMENT_CHANNEL + CHANNEL_NAME = ELECTRICAL_MEASUREMENT_CHANNEL async def async_update(self): """Retrieve latest state.""" _LOGGER.debug("%s async_update", self.unique_id) # This is a polling channel. Don't allow cache. - result = await self.get_attribute_value( - ELECTRICAL_MEASUREMENT_CHANNEL, from_cache=False) + result = await self.get_attribute_value('active_power', + from_cache=False) async_dispatcher_send( self._zha_device.hass, "{}_{}".format(self.unique_id, SIGNAL_ATTR_UPDATED), @@ -35,6 +32,5 @@ async def async_update(self): async def async_initialize(self, from_cache): """Initialize channel.""" - await self.get_attribute_value( - ELECTRICAL_MEASUREMENT_CHANNEL, from_cache=from_cache) + await self.get_attribute_value('active_power', from_cache=from_cache) await super().async_initialize(from_cache) diff --git a/homeassistant/components/zha/core/channels/hvac.py b/homeassistant/components/zha/core/channels/hvac.py index c62ec66588e80c..3da881e75d8f7a 100644 --- a/homeassistant/components/zha/core/channels/hvac.py +++ b/homeassistant/components/zha/core/channels/hvac.py @@ -8,7 +8,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send from . import ZigbeeChannel -from ..const import FAN_CHANNEL, SIGNAL_ATTR_UPDATED +from ..const import SIGNAL_ATTR_UPDATED _LOGGER = logging.getLogger(__name__) @@ -18,11 +18,6 @@ class FanChannel(ZigbeeChannel): _value_attribute = 0 - def __init__(self, cluster, device): - """Initialize FanChannel.""" - super().__init__(cluster, device) - self.name = FAN_CHANNEL - async def async_set_speed(self, value) -> None: """Set the speed of the fan.""" from zigpy.exceptions import DeliveryError diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index 9c904a7a001ba1..696a15b483be8a 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -6,7 +6,6 @@ """ import logging from . import ZigbeeChannel -from ..const import COLOR_CHANNEL _LOGGER = logging.getLogger(__name__) @@ -21,7 +20,6 @@ class ColorChannel(ZigbeeChannel): def __init__(self, cluster, device): """Initialize ColorChannel.""" super().__init__(cluster, device) - self.name = COLOR_CHANNEL self._color_capabilities = None def get_color_capabilities(self): diff --git a/homeassistant/components/zha/core/channels/security.py b/homeassistant/components/zha/core/channels/security.py index e8c0e71a26378a..03b50b7c7ba803 100644 --- a/homeassistant/components/zha/core/channels/security.py +++ b/homeassistant/components/zha/core/channels/security.py @@ -9,7 +9,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from . import ZigbeeChannel from ..helpers import bind_cluster -from ..const import SIGNAL_ATTR_UPDATED, ZONE_CHANNEL +from ..const import SIGNAL_ATTR_UPDATED _LOGGER = logging.getLogger(__name__) @@ -17,11 +17,6 @@ class IASZoneChannel(ZigbeeChannel): """Channel for the IASZone Zigbee cluster.""" - def __init__(self, cluster, device): - """Initialize IASZoneChannel.""" - super().__init__(cluster, device) - self.name = ZONE_CHANNEL - @callback def cluster_command(self, tsn, command_id, args): """Handle commands received to this cluster.""" diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 58cecb3600f240..b7f418253d8d80 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -87,12 +87,12 @@ ON_OFF_CHANNEL = 'on_off' ATTRIBUTE_CHANNEL = 'attribute' BASIC_CHANNEL = 'basic' -COLOR_CHANNEL = 'color' +COLOR_CHANNEL = 'light_color' FAN_CHANNEL = 'fan' LEVEL_CHANNEL = ATTR_LEVEL -ZONE_CHANNEL = 'zone' -ELECTRICAL_MEASUREMENT_CHANNEL = 'active_power' -POWER_CONFIGURATION_CHANNEL = 'battery' +ZONE_CHANNEL = 'ias_zone' +ELECTRICAL_MEASUREMENT_CHANNEL = 'electrical_measurement' +POWER_CONFIGURATION_CHANNEL = 'power' EVENT_RELAY_CHANNEL = 'event_relay' SIGNAL_ATTR_UPDATED = 'attribute_updated' From f64a99878f2adbf384a9cd308132729c9ad3a961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?= Date: Thu, 21 Mar 2019 16:24:30 +0100 Subject: [PATCH 067/605] Allow on/off on tado climate component. (#22242) * Allow on/off on tado climate component. Bump python-tado version. Patch from @wmalgadey * Revert wrongly change in tado device tracker --- homeassistant/components/tado/__init__.py | 2 +- homeassistant/components/tado/climate.py | 26 +++++++++++++++++-- .../components/tado/device_tracker.py | 4 +-- requirements_all.txt | 2 +- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 56fc0cb704c573..6808729685eb11 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,7 +10,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.util import Throttle -REQUIREMENTS = ['python-tado==0.2.8'] +REQUIREMENTS = ['python-tado==0.2.9'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 71ebeadaeed022..56c670184b5b44 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -3,7 +3,7 @@ from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) + SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_ON_OFF) from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.util.temperature import convert as convert_temperature @@ -42,7 +42,8 @@ CONST_MODE_OFF: 'Off', } -SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE +SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE | + SUPPORT_ON_OFF) def setup_platform(hass, config, add_entities, discovery_info=None): @@ -200,6 +201,27 @@ def target_temperature(self): """Return the temperature we try to reach.""" return self._target_temp + @property + def is_on(self): + """Return true if heater is on.""" + return self._device_is_active + + def turn_off(self): + """Turn device off.""" + _LOGGER.info("Switching mytado.com to OFF for zone %s", + self.zone_name) + + self._current_operation = CONST_MODE_OFF + self._control_heating() + + def turn_on(self): + """Turn device on.""" + _LOGGER.info("Switching mytado.com to %s mode for zone %s", + self._overlay_mode, self.zone_name) + + self._current_operation = self._overlay_mode + self._control_heating() + def set_temperature(self, **kwargs): """Set new target temperature.""" temperature = kwargs.get(ATTR_TEMPERATURE) diff --git a/homeassistant/components/tado/device_tracker.py b/homeassistant/components/tado/device_tracker.py index 8804bef561622a..7812bbd812bbd5 100644 --- a/homeassistant/components/tado/device_tracker.py +++ b/homeassistant/components/tado/device_tracker.py @@ -52,9 +52,9 @@ def __init__(self, hass, config): # If there's a home_id, we need a different API URL if self.home_id is None: - self.tadoapiurl = 'https://auth.tado.com/api/v2/me' + self.tadoapiurl = 'https://my.tado.com/api/v2/me' else: - self.tadoapiurl = 'https://auth.tado.com/api/v2' \ + self.tadoapiurl = 'https://my.tado.com/api/v2' \ '/homes/{home_id}/mobileDevices' # The API URL always needs a username and password diff --git a/requirements_all.txt b/requirements_all.txt index 3854fd2c00fe41..da790b33b99b84 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1391,7 +1391,7 @@ python-songpal==0.0.9.1 python-synology==0.2.0 # homeassistant.components.tado -python-tado==0.2.8 +python-tado==0.2.9 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 From 6526c68e2d66f0a8f093cbdee279cd1968c242f2 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Thu, 21 Mar 2019 11:13:20 -0500 Subject: [PATCH 068/605] Fix validate webhook requirements (#22248) --- .../components/smartthings/smartapp.py | 2 ++ tests/components/smartthings/test_init.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index 0b64bac595676f..548a38711bd176 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -65,6 +65,8 @@ def validate_webhook_requirements(hass: HomeAssistantType) -> bool: """Ensure HASS is setup properly to receive webhooks.""" if cloud.async_active_subscription(hass): return True + if hass.data[DOMAIN][CONF_CLOUDHOOK_URL] is not None: + return True return get_webhook_url(hass).lower().startswith('https://') diff --git a/tests/components/smartthings/test_init.py b/tests/components/smartthings/test_init.py index a5edc93fce64bc..4daf37cac550a1 100644 --- a/tests/components/smartthings/test_init.py +++ b/tests/components/smartthings/test_init.py @@ -189,6 +189,33 @@ async def test_config_entry_loads_platforms( assert forward_mock.call_count == len(SUPPORTED_PLATFORMS) +async def test_config_entry_loads_unconnected_cloud( + hass, config_entry, app, installed_app, + device, smartthings_mock, subscription_factory, scene): + """Test entry loads during startup when cloud isn't connected.""" + setattr(hass.config_entries, '_entries', [config_entry]) + hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud" + hass.config.api.base_url = 'http://0.0.0.0' + api = smartthings_mock.return_value + api.app.return_value = mock_coro(return_value=app) + api.installed_app.return_value = mock_coro(return_value=installed_app) + api.devices.side_effect = \ + lambda *args, **kwargs: mock_coro(return_value=[device]) + api.scenes.return_value = mock_coro(return_value=[scene]) + mock_token = Mock() + mock_token.access_token.return_value = str(uuid4()) + mock_token.refresh_token.return_value = str(uuid4()) + api.generate_tokens.return_value = mock_coro(return_value=mock_token) + subscriptions = [subscription_factory(capability) + for capability in device.capabilities] + api.subscriptions.return_value = mock_coro(return_value=subscriptions) + with patch.object(hass.config_entries, 'async_forward_entry_setup', + return_value=mock_coro()) as forward_mock: + assert await smartthings.async_setup_entry(hass, config_entry) + await hass.async_block_till_done() + assert forward_mock.call_count == len(SUPPORTED_PLATFORMS) + + async def test_unload_entry(hass, config_entry): """Test entries are unloaded correctly.""" connect_disconnect = Mock() From fe468ace34e6301b5a583130e98f565e6b506c57 Mon Sep 17 00:00:00 2001 From: Karim Roukoz Date: Thu, 21 Mar 2019 12:39:30 -0400 Subject: [PATCH 069/605] Bump total-connect-client to 0.25, fixing issue with Total Connect (#22230) * Bump total-connect-client to 0.25 * Bump version in requirements_all.txt --- homeassistant/components/totalconnect/alarm_control_panel.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index ba8155fde930f5..a272a22abe509d 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -18,7 +18,7 @@ STATE_ALARM_ARMED_CUSTOM_BYPASS) -REQUIREMENTS = ['total_connect_client==0.24'] +REQUIREMENTS = ['total_connect_client==0.25'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index da790b33b99b84..bb40704d0d1183 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1707,7 +1707,7 @@ todoist-python==7.0.17 toonapilib==3.2.2 # homeassistant.components.totalconnect.alarm_control_panel -total_connect_client==0.24 +total_connect_client==0.25 # homeassistant.components.tplink_lte tp-connected==0.0.4 From 81bb92839424074e7462c3731f086855cdf91621 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Fri, 22 Mar 2019 01:57:42 +0800 Subject: [PATCH 070/605] Handle on/off through TemperatrureSetting trait. (#21842) --- .../components/google_assistant/trait.py | 56 +++++++++--- tests/components/google_assistant/__init__.py | 5 +- .../google_assistant/test_google_assistant.py | 2 - .../components/google_assistant/test_trait.py | 88 +++++++++++-------- 4 files changed, 93 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index aff24f3051257f..f5b959285db4fe 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -21,6 +21,7 @@ SERVICE_TURN_ON, STATE_LOCKED, STATE_OFF, + STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, @@ -199,8 +200,6 @@ class OnOffTrait(_Trait): @staticmethod def supported(domain, features): """Test if state is supported.""" - if domain == climate.DOMAIN: - return features & climate.SUPPORT_ON_OFF != 0 return domain in ( group.DOMAIN, input_boolean.DOMAIN, @@ -537,10 +536,18 @@ def supported(domain, features): def sync_attributes(self): """Return temperature point and modes attributes for a sync request.""" modes = [] - for mode in self.state.attributes.get(climate.ATTR_OPERATION_LIST, []): - google_mode = self.hass_to_google.get(mode) - if google_mode is not None: - modes.append(google_mode) + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if supported & climate.SUPPORT_ON_OFF != 0: + modes.append(STATE_OFF) + modes.append(STATE_ON) + + if supported & climate.SUPPORT_OPERATION_MODE != 0: + for mode in self.state.attributes.get(climate.ATTR_OPERATION_LIST, + []): + google_mode = self.hass_to_google.get(mode) + if google_mode and google_mode not in modes: + modes.append(google_mode) return { 'availableThermostatModes': ','.join(modes), @@ -554,8 +561,16 @@ def query_attributes(self): response = {} operation = attrs.get(climate.ATTR_OPERATION_MODE) - if operation is not None and operation in self.hass_to_google: + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if (supported & climate.SUPPORT_ON_OFF + and self.state.state == STATE_OFF): + response['thermostatMode'] = 'off' + elif (supported & climate.SUPPORT_OPERATION_MODE and + operation in self.hass_to_google): response['thermostatMode'] = self.hass_to_google[operation] + elif supported & climate.SUPPORT_ON_OFF: + response['thermostatMode'] = 'on' unit = self.hass.config.units.temperature_unit @@ -644,12 +659,27 @@ async def execute(self, command, data, params): }, blocking=True, context=data.context) elif command == COMMAND_THERMOSTAT_SET_MODE: - await self.hass.services.async_call( - climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { - ATTR_ENTITY_ID: self.state.entity_id, - climate.ATTR_OPERATION_MODE: - self.google_to_hass[params['thermostatMode']], - }, blocking=True, context=data.context) + target_mode = params['thermostatMode'] + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + + if (target_mode in [STATE_ON, STATE_OFF] and + supported & climate.SUPPORT_ON_OFF): + await self.hass.services.async_call( + climate.DOMAIN, + (SERVICE_TURN_ON + if target_mode == STATE_ON + else SERVICE_TURN_OFF), + { + ATTR_ENTITY_ID: self.state.entity_id, + climate.ATTR_OPERATION_MODE: target_mode, + }, blocking=True, context=data.context) + elif supported & climate.SUPPORT_OPERATION_MODE: + await self.hass.services.async_call( + climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { + ATTR_ENTITY_ID: self.state.entity_id, + climate.ATTR_OPERATION_MODE: + self.google_to_hass[target_mode], + }, blocking=True, context=data.context) @register_trait diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index 03cc327a5c51f7..a8ea4a3f8881d0 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -231,10 +231,7 @@ 'name': { 'name': 'HeatPump' }, - 'traits': [ - 'action.devices.traits.OnOff', - 'action.devices.traits.TemperatureSetting' - ], + 'traits': ['action.devices.traits.TemperatureSetting'], 'type': 'action.devices.types.THERMOSTAT', 'willReportState': False }, { diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 18f99c8268524a..fbaf1b47898528 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -205,7 +205,6 @@ def test_query_climate_request(hass_fixture, assistant_client, auth_header): devices = body['payload']['devices'] assert len(devices) == 3 assert devices['climate.heatpump'] == { - 'on': True, 'online': True, 'thermostatTemperatureSetpoint': 20.0, 'thermostatTemperatureAmbient': 25.0, @@ -262,7 +261,6 @@ def test_query_climate_request_f(hass_fixture, assistant_client, auth_header): devices = body['payload']['devices'] assert len(devices) == 3 assert devices['climate.heatpump'] == { - 'on': True, 'online': True, 'thermostatTemperatureSetpoint': -6.7, 'thermostatTemperatureAmbient': -3.9, diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 301de9c8c256b8..3af60e2f014c0e 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -408,44 +408,9 @@ async def test_onoff_media_player(hass): async def test_onoff_climate(hass): - """Test OnOff trait support for climate domain.""" - assert trait.OnOffTrait.supported(climate.DOMAIN, climate.SUPPORT_ON_OFF) - - trt_on = trait.OnOffTrait(hass, State('climate.bla', STATE_ON), - BASIC_CONFIG) - - assert trt_on.sync_attributes() == {} - - assert trt_on.query_attributes() == { - 'on': True - } - - trt_off = trait.OnOffTrait(hass, State('climate.bla', STATE_OFF), - BASIC_CONFIG) - - assert trt_off.query_attributes() == { - 'on': False - } - - on_calls = async_mock_service(hass, climate.DOMAIN, SERVICE_TURN_ON) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': True}) - assert len(on_calls) == 1 - assert on_calls[0].data == { - ATTR_ENTITY_ID: 'climate.bla', - } - - off_calls = async_mock_service(hass, climate.DOMAIN, - SERVICE_TURN_OFF) - - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': False}) - assert len(off_calls) == 1 - assert off_calls[0].data == { - ATTR_ENTITY_ID: 'climate.bla', - } + """Test OnOff trait not supported for climate domain.""" + assert not trait.OnOffTrait.supported( + climate.DOMAIN, climate.SUPPORT_ON_OFF) async def test_dock_vacuum(hass): @@ -673,6 +638,48 @@ async def test_scene_script(hass): } +async def test_temperature_setting_climate_onoff(hass): + """Test TemperatureSetting trait support for climate domain - range.""" + assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) + assert trait.TemperatureSettingTrait.supported( + climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) + + hass.config.units.temperature_unit = TEMP_FAHRENHEIT + + trt = trait.TemperatureSettingTrait(hass, State( + 'climate.bla', climate.STATE_AUTO, { + ATTR_SUPPORTED_FEATURES: ( + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), + climate.ATTR_OPERATION_MODE: climate.STATE_COOL, + climate.ATTR_OPERATION_LIST: [ + climate.STATE_COOL, + climate.STATE_HEAT, + climate.STATE_AUTO, + ], + climate.ATTR_MIN_TEMP: None, + climate.ATTR_MAX_TEMP: None, + }), BASIC_CONFIG) + assert trt.sync_attributes() == { + 'availableThermostatModes': 'off,on,cool,heat,heatcool', + 'thermostatTemperatureUnit': 'F', + } + assert trt.can_execute(trait.COMMAND_THERMOSTAT_SET_MODE, {}) + + calls = async_mock_service( + hass, climate.DOMAIN, SERVICE_TURN_ON) + await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, BASIC_DATA, { + 'thermostatMode': 'on', + }) + assert len(calls) == 1 + + calls = async_mock_service( + hass, climate.DOMAIN, SERVICE_TURN_OFF) + await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, BASIC_DATA, { + 'thermostatMode': 'off', + }) + assert len(calls) == 1 + + async def test_temperature_setting_climate_range(hass): """Test TemperatureSetting trait support for climate domain - range.""" assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) @@ -685,6 +692,7 @@ async def test_temperature_setting_climate_range(hass): 'climate.bla', climate.STATE_AUTO, { climate.ATTR_CURRENT_TEMPERATURE: 70, climate.ATTR_CURRENT_HUMIDITY: 25, + ATTR_SUPPORTED_FEATURES: climate.SUPPORT_OPERATION_MODE, climate.ATTR_OPERATION_MODE: climate.STATE_AUTO, climate.ATTR_OPERATION_LIST: [ STATE_OFF, @@ -755,6 +763,8 @@ async def test_temperature_setting_climate_setpoint(hass): trt = trait.TemperatureSettingTrait(hass, State( 'climate.bla', climate.STATE_AUTO, { + ATTR_SUPPORTED_FEATURES: ( + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), climate.ATTR_OPERATION_MODE: climate.STATE_COOL, climate.ATTR_OPERATION_LIST: [ STATE_OFF, @@ -766,7 +776,7 @@ async def test_temperature_setting_climate_setpoint(hass): climate.ATTR_CURRENT_TEMPERATURE: 20 }), BASIC_CONFIG) assert trt.sync_attributes() == { - 'availableThermostatModes': 'off,cool', + 'availableThermostatModes': 'off,on,cool', 'thermostatTemperatureUnit': 'C', } assert trt.query_attributes() == { From 1b66520e31a338df63299949c38b06e768bf49bf Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 Mar 2019 19:39:24 +0100 Subject: [PATCH 071/605] Update Hass-NabuCasa 0.9 (#22258) --- homeassistant/components/cloud/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index ff1b2344ac89c8..75874d6759ef48 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.8'] +REQUIREMENTS = ['hass-nabucasa==0.9'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index bb40704d0d1183..56339fe7bda8b5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -518,7 +518,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.8 +hass-nabucasa==0.9 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 98ef1dbab43e6a..68119e6b54e52c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.8 +hass-nabucasa==0.9 # homeassistant.components.mqtt.server hbmqtt==0.9.4 From 0a4251e08f6410acea9c3436ad90765253f34eb7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2019 12:56:59 -0700 Subject: [PATCH 072/605] Updated frontend to 20190321.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 6e05299ec52b6c..30b9d350df6d1d 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190320.0'] +REQUIREMENTS = ['home-assistant-frontend==20190321.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 56339fe7bda8b5..349f8d483045ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -548,7 +548,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190320.0 +home-assistant-frontend==20190321.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 68119e6b54e52c..b88e6b97db2d2b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190320.0 +home-assistant-frontend==20190321.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 72bb94de960ab17b7370b03f482773638f6f9cce Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2019 12:57:20 -0700 Subject: [PATCH 073/605] Update translations --- .../ambient_station/.translations/bg.json | 19 +++++++++++ .../components/auth/.translations/bg.json | 16 +++++++++ .../components/cast/.translations/th.json | 9 +++++ .../components/daikin/.translations/bg.json | 11 ++++++ .../daikin/.translations/es-419.json | 2 ++ .../components/deconz/.translations/bg.json | 2 +- .../components/deconz/.translations/fr.json | 2 +- .../components/deconz/.translations/th.json | 11 ++++++ .../dialogflow/.translations/bg.json | 7 ++++ .../dialogflow/.translations/pl.json | 2 +- .../components/ebusd/.translations/bg.json | 6 ++++ .../components/ebusd/.translations/fr.json | 6 ++++ .../emulated_roku/.translations/bg.json | 11 ++++++ .../emulated_roku/.translations/fr.json | 11 ++++++ .../components/esphome/.translations/bg.json | 34 +++++++++++++++++++ .../esphome/.translations/es-419.json | 3 ++ .../components/esphome/.translations/fr.json | 2 +- .../components/esphome/.translations/pl.json | 2 +- .../components/esphome/.translations/uk.json | 4 +++ .../components/geofency/.translations/bg.json | 7 ++++ .../components/geofency/.translations/fr.json | 3 +- .../components/geofency/.translations/pl.json | 2 +- .../gpslogger/.translations/bg.json | 7 ++++ .../gpslogger/.translations/pl.json | 2 +- .../components/hangouts/.translations/th.json | 3 ++ .../homekit_controller/.translations/bg.json | 33 ++++++++++++++++++ .../homekit_controller/.translations/de.json | 2 +- .../.translations/es-419.json | 15 ++++++++ .../homekit_controller/.translations/fr.json | 33 ++++++++++++++++++ .../homematicip_cloud/.translations/th.json | 14 ++++++++ .../components/hue/.translations/bg.json | 2 +- .../components/hue/.translations/th.json | 5 +++ .../components/ifttt/.translations/bg.json | 18 ++++++++++ .../components/ifttt/.translations/pl.json | 2 +- .../components/ios/.translations/bg.json | 14 ++++++++ .../components/ipma/.translations/bg.json | 19 +++++++++++ .../components/ipma/.translations/fr.json | 13 +++++++ .../components/locative/.translations/bg.json | 18 ++++++++++ .../components/locative/.translations/fr.json | 12 ++++++- .../components/locative/.translations/pl.json | 2 +- .../luftdaten/.translations/bg.json | 19 +++++++++++ .../luftdaten/.translations/fr.json | 18 ++++++++++ .../components/mailgun/.translations/bg.json | 7 ++++ .../components/mailgun/.translations/pl.json | 2 +- .../mobile_app/.translations/ca.json | 14 ++++++++ .../mobile_app/.translations/de.json | 14 ++++++++ .../mobile_app/.translations/en.json | 24 ++++++------- .../mobile_app/.translations/es-419.json | 10 ++++++ .../mobile_app/.translations/ko.json | 14 ++++++++ .../mobile_app/.translations/lb.json | 14 ++++++++ .../mobile_app/.translations/no.json | 14 ++++++++ .../mobile_app/.translations/pl.json | 14 ++++++++ .../mobile_app/.translations/ru.json | 14 ++++++++ .../mobile_app/.translations/uk.json | 11 ++++++ .../mobile_app/.translations/zh-Hant.json | 14 ++++++++ .../moon/.translations/sensor.ca.json | 2 +- .../moon/.translations/sensor.no.json | 10 +++--- .../moon/.translations/sensor.uk.json | 6 +--- .../components/mqtt/.translations/bg.json | 31 +++++++++++++++++ .../components/nest/.translations/es-419.json | 1 + .../owntracks/.translations/bg.json | 17 ++++++++++ .../owntracks/.translations/fr.json | 14 ++++++++ .../owntracks/.translations/pl.json | 2 +- .../components/point/.translations/fr.json | 19 +++++++++++ .../components/ps4/.translations/bg.json | 32 +++++++++++++++++ .../components/ps4/.translations/fr.json | 12 +++++++ .../rainmachine/.translations/bg.json | 11 ++++++ .../rainmachine/.translations/fr.json | 19 +++++++++++ .../season/.translations/sensor.pl.json | 8 ++--- .../sensor/.translations/moon.ar.json | 6 ++++ .../sensor/.translations/moon.bg.json | 12 +++++++ .../sensor/.translations/moon.ca.json | 12 +++++++ .../sensor/.translations/moon.cs.json | 12 +++++++ .../sensor/.translations/moon.da.json | 12 +++++++ .../sensor/.translations/moon.de.json | 12 +++++++ .../sensor/.translations/moon.en.json | 12 +++++++ .../sensor/.translations/moon.es-419.json | 12 +++++++ .../sensor/.translations/moon.es.json | 10 ++++++ .../sensor/.translations/moon.et.json | 12 +++++++ .../sensor/.translations/moon.fi.json | 12 +++++++ .../sensor/.translations/moon.fr.json | 12 +++++++ .../sensor/.translations/moon.he.json | 12 +++++++ .../sensor/.translations/moon.hu.json | 12 +++++++ .../sensor/.translations/moon.id.json | 12 +++++++ .../sensor/.translations/moon.it.json | 12 +++++++ .../sensor/.translations/moon.ko.json | 12 +++++++ .../sensor/.translations/moon.lb.json | 12 +++++++ .../sensor/.translations/moon.nl.json | 12 +++++++ .../sensor/.translations/moon.nn.json | 12 +++++++ .../sensor/.translations/moon.no.json | 12 +++++++ .../sensor/.translations/moon.pl.json | 12 +++++++ .../sensor/.translations/moon.pt-BR.json | 12 +++++++ .../sensor/.translations/moon.pt.json | 12 +++++++ .../sensor/.translations/moon.ro.json | 6 ++++ .../sensor/.translations/moon.ru.json | 12 +++++++ .../sensor/.translations/moon.sl.json | 12 +++++++ .../sensor/.translations/moon.sv.json | 12 +++++++ .../sensor/.translations/moon.uk.json | 12 +++++++ .../sensor/.translations/moon.zh-Hans.json | 12 +++++++ .../sensor/.translations/moon.zh-Hant.json | 12 +++++++ .../sensor/.translations/season.af.json | 8 +++++ .../sensor/.translations/season.bg.json | 8 +++++ .../sensor/.translations/season.ca.json | 8 +++++ .../sensor/.translations/season.cs.json | 8 +++++ .../sensor/.translations/season.cy.json | 8 +++++ .../sensor/.translations/season.da.json | 8 +++++ .../sensor/.translations/season.de.json | 8 +++++ .../sensor/.translations/season.en.json | 8 +++++ .../sensor/.translations/season.es-419.json | 8 +++++ .../sensor/.translations/season.es.json | 8 +++++ .../sensor/.translations/season.et.json | 8 +++++ .../sensor/.translations/season.eu.json | 8 +++++ .../sensor/.translations/season.fi.json | 8 +++++ .../sensor/.translations/season.fr.json | 8 +++++ .../sensor/.translations/season.he.json | 8 +++++ .../sensor/.translations/season.hu.json | 8 +++++ .../sensor/.translations/season.id.json | 8 +++++ .../sensor/.translations/season.it.json | 8 +++++ .../sensor/.translations/season.ja.json | 8 +++++ .../sensor/.translations/season.ko.json | 8 +++++ .../sensor/.translations/season.lb.json | 8 +++++ .../sensor/.translations/season.lv.json | 8 +++++ .../sensor/.translations/season.nl.json | 8 +++++ .../sensor/.translations/season.nn.json | 8 +++++ .../sensor/.translations/season.no.json | 8 +++++ .../sensor/.translations/season.pl.json | 8 +++++ .../sensor/.translations/season.pt-BR.json | 8 +++++ .../sensor/.translations/season.pt.json | 8 +++++ .../sensor/.translations/season.ro.json | 8 +++++ .../sensor/.translations/season.ru.json | 8 +++++ .../sensor/.translations/season.sl.json | 8 +++++ .../sensor/.translations/season.sv.json | 8 +++++ .../sensor/.translations/season.th.json | 8 +++++ .../sensor/.translations/season.uk.json | 8 +++++ .../sensor/.translations/season.vi.json | 8 +++++ .../sensor/.translations/season.zh-Hans.json | 8 +++++ .../sensor/.translations/season.zh-Hant.json | 8 +++++ .../smartthings/.translations/bg.json | 28 +++++++++++++++ .../smartthings/.translations/fr.json | 6 +++- .../components/sonos/.translations/th.json | 9 +++++ .../tellduslive/.translations/bg.json | 11 ++++++ .../tellduslive/.translations/fr.json | 16 +++++++-- .../components/toon/.translations/bg.json | 33 ++++++++++++++++++ .../components/toon/.translations/es-419.json | 3 +- .../components/toon/.translations/fr.json | 2 ++ .../components/tplink/.translations/bg.json | 15 ++++++++ .../components/tradfri/.translations/bg.json | 23 +++++++++++++ .../components/twilio/.translations/bg.json | 7 ++++ .../components/twilio/.translations/pl.json | 2 +- .../components/unifi/.translations/bg.json | 11 ++++++ .../components/upnp/.translations/bg.json | 24 +++++++++++++ .../components/zha/.translations/fr.json | 19 +++++++++++ .../components/zwave/.translations/bg.json | 22 ++++++++++++ .../components/zwave/.translations/pl.json | 2 +- 154 files changed, 1612 insertions(+), 48 deletions(-) create mode 100644 homeassistant/components/ambient_station/.translations/bg.json create mode 100644 homeassistant/components/auth/.translations/bg.json create mode 100644 homeassistant/components/cast/.translations/th.json create mode 100644 homeassistant/components/daikin/.translations/bg.json create mode 100644 homeassistant/components/deconz/.translations/th.json create mode 100644 homeassistant/components/dialogflow/.translations/bg.json create mode 100644 homeassistant/components/ebusd/.translations/bg.json create mode 100644 homeassistant/components/ebusd/.translations/fr.json create mode 100644 homeassistant/components/emulated_roku/.translations/bg.json create mode 100644 homeassistant/components/esphome/.translations/bg.json create mode 100644 homeassistant/components/geofency/.translations/bg.json create mode 100644 homeassistant/components/gpslogger/.translations/bg.json create mode 100644 homeassistant/components/homekit_controller/.translations/bg.json create mode 100644 homeassistant/components/homekit_controller/.translations/es-419.json create mode 100644 homeassistant/components/homekit_controller/.translations/fr.json create mode 100644 homeassistant/components/homematicip_cloud/.translations/th.json create mode 100644 homeassistant/components/hue/.translations/th.json create mode 100644 homeassistant/components/ifttt/.translations/bg.json create mode 100644 homeassistant/components/ios/.translations/bg.json create mode 100644 homeassistant/components/ipma/.translations/bg.json create mode 100644 homeassistant/components/ipma/.translations/fr.json create mode 100644 homeassistant/components/locative/.translations/bg.json create mode 100644 homeassistant/components/luftdaten/.translations/bg.json create mode 100644 homeassistant/components/luftdaten/.translations/fr.json create mode 100644 homeassistant/components/mailgun/.translations/bg.json create mode 100644 homeassistant/components/mobile_app/.translations/ca.json create mode 100644 homeassistant/components/mobile_app/.translations/de.json create mode 100644 homeassistant/components/mobile_app/.translations/es-419.json create mode 100644 homeassistant/components/mobile_app/.translations/ko.json create mode 100644 homeassistant/components/mobile_app/.translations/lb.json create mode 100644 homeassistant/components/mobile_app/.translations/no.json create mode 100644 homeassistant/components/mobile_app/.translations/pl.json create mode 100644 homeassistant/components/mobile_app/.translations/ru.json create mode 100644 homeassistant/components/mobile_app/.translations/uk.json create mode 100644 homeassistant/components/mobile_app/.translations/zh-Hant.json create mode 100644 homeassistant/components/mqtt/.translations/bg.json create mode 100644 homeassistant/components/owntracks/.translations/bg.json create mode 100644 homeassistant/components/owntracks/.translations/fr.json create mode 100644 homeassistant/components/point/.translations/fr.json create mode 100644 homeassistant/components/ps4/.translations/bg.json create mode 100644 homeassistant/components/rainmachine/.translations/bg.json create mode 100644 homeassistant/components/rainmachine/.translations/fr.json create mode 100644 homeassistant/components/sensor/.translations/moon.ar.json create mode 100644 homeassistant/components/sensor/.translations/moon.bg.json create mode 100644 homeassistant/components/sensor/.translations/moon.ca.json create mode 100644 homeassistant/components/sensor/.translations/moon.cs.json create mode 100644 homeassistant/components/sensor/.translations/moon.da.json create mode 100644 homeassistant/components/sensor/.translations/moon.de.json create mode 100644 homeassistant/components/sensor/.translations/moon.en.json create mode 100644 homeassistant/components/sensor/.translations/moon.es-419.json create mode 100644 homeassistant/components/sensor/.translations/moon.es.json create mode 100644 homeassistant/components/sensor/.translations/moon.et.json create mode 100644 homeassistant/components/sensor/.translations/moon.fi.json create mode 100644 homeassistant/components/sensor/.translations/moon.fr.json create mode 100644 homeassistant/components/sensor/.translations/moon.he.json create mode 100644 homeassistant/components/sensor/.translations/moon.hu.json create mode 100644 homeassistant/components/sensor/.translations/moon.id.json create mode 100644 homeassistant/components/sensor/.translations/moon.it.json create mode 100644 homeassistant/components/sensor/.translations/moon.ko.json create mode 100644 homeassistant/components/sensor/.translations/moon.lb.json create mode 100644 homeassistant/components/sensor/.translations/moon.nl.json create mode 100644 homeassistant/components/sensor/.translations/moon.nn.json create mode 100644 homeassistant/components/sensor/.translations/moon.no.json create mode 100644 homeassistant/components/sensor/.translations/moon.pl.json create mode 100644 homeassistant/components/sensor/.translations/moon.pt-BR.json create mode 100644 homeassistant/components/sensor/.translations/moon.pt.json create mode 100644 homeassistant/components/sensor/.translations/moon.ro.json create mode 100644 homeassistant/components/sensor/.translations/moon.ru.json create mode 100644 homeassistant/components/sensor/.translations/moon.sl.json create mode 100644 homeassistant/components/sensor/.translations/moon.sv.json create mode 100644 homeassistant/components/sensor/.translations/moon.uk.json create mode 100644 homeassistant/components/sensor/.translations/moon.zh-Hans.json create mode 100644 homeassistant/components/sensor/.translations/moon.zh-Hant.json create mode 100644 homeassistant/components/sensor/.translations/season.af.json create mode 100644 homeassistant/components/sensor/.translations/season.bg.json create mode 100644 homeassistant/components/sensor/.translations/season.ca.json create mode 100644 homeassistant/components/sensor/.translations/season.cs.json create mode 100644 homeassistant/components/sensor/.translations/season.cy.json create mode 100644 homeassistant/components/sensor/.translations/season.da.json create mode 100644 homeassistant/components/sensor/.translations/season.de.json create mode 100644 homeassistant/components/sensor/.translations/season.en.json create mode 100644 homeassistant/components/sensor/.translations/season.es-419.json create mode 100644 homeassistant/components/sensor/.translations/season.es.json create mode 100644 homeassistant/components/sensor/.translations/season.et.json create mode 100644 homeassistant/components/sensor/.translations/season.eu.json create mode 100644 homeassistant/components/sensor/.translations/season.fi.json create mode 100644 homeassistant/components/sensor/.translations/season.fr.json create mode 100644 homeassistant/components/sensor/.translations/season.he.json create mode 100644 homeassistant/components/sensor/.translations/season.hu.json create mode 100644 homeassistant/components/sensor/.translations/season.id.json create mode 100644 homeassistant/components/sensor/.translations/season.it.json create mode 100644 homeassistant/components/sensor/.translations/season.ja.json create mode 100644 homeassistant/components/sensor/.translations/season.ko.json create mode 100644 homeassistant/components/sensor/.translations/season.lb.json create mode 100644 homeassistant/components/sensor/.translations/season.lv.json create mode 100644 homeassistant/components/sensor/.translations/season.nl.json create mode 100644 homeassistant/components/sensor/.translations/season.nn.json create mode 100644 homeassistant/components/sensor/.translations/season.no.json create mode 100644 homeassistant/components/sensor/.translations/season.pl.json create mode 100644 homeassistant/components/sensor/.translations/season.pt-BR.json create mode 100644 homeassistant/components/sensor/.translations/season.pt.json create mode 100644 homeassistant/components/sensor/.translations/season.ro.json create mode 100644 homeassistant/components/sensor/.translations/season.ru.json create mode 100644 homeassistant/components/sensor/.translations/season.sl.json create mode 100644 homeassistant/components/sensor/.translations/season.sv.json create mode 100644 homeassistant/components/sensor/.translations/season.th.json create mode 100644 homeassistant/components/sensor/.translations/season.uk.json create mode 100644 homeassistant/components/sensor/.translations/season.vi.json create mode 100644 homeassistant/components/sensor/.translations/season.zh-Hans.json create mode 100644 homeassistant/components/sensor/.translations/season.zh-Hant.json create mode 100644 homeassistant/components/smartthings/.translations/bg.json create mode 100644 homeassistant/components/sonos/.translations/th.json create mode 100644 homeassistant/components/tellduslive/.translations/bg.json create mode 100644 homeassistant/components/toon/.translations/bg.json create mode 100644 homeassistant/components/tplink/.translations/bg.json create mode 100644 homeassistant/components/tradfri/.translations/bg.json create mode 100644 homeassistant/components/twilio/.translations/bg.json create mode 100644 homeassistant/components/unifi/.translations/bg.json create mode 100644 homeassistant/components/upnp/.translations/bg.json create mode 100644 homeassistant/components/zha/.translations/fr.json create mode 100644 homeassistant/components/zwave/.translations/bg.json diff --git a/homeassistant/components/ambient_station/.translations/bg.json b/homeassistant/components/ambient_station/.translations/bg.json new file mode 100644 index 00000000000000..2099038f00430c --- /dev/null +++ b/homeassistant/components/ambient_station/.translations/bg.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "identifier_exists": "Application \u0438/\u0438\u043b\u0438 API \u043a\u043b\u044e\u0447\u044a\u0442 \u0432\u0435\u0447\u0435 \u0441\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d\u0438", + "invalid_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447 \u0438/\u0438\u043b\u0438 Application \u043a\u043b\u044e\u0447", + "no_devices": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043f\u0440\u043e\u0444\u0438\u043b\u0430" + }, + "step": { + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "app_key": "Application \u043a\u043b\u044e\u0447" + }, + "title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0441\u0438" + } + }, + "title": "\u0410\u0442\u043c\u043e\u0441\u0444\u0435\u0440\u043d\u0430 PWS" + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/bg.json b/homeassistant/components/auth/.translations/bg.json new file mode 100644 index 00000000000000..63cf17f0b2282b --- /dev/null +++ b/homeassistant/components/auth/.translations/bg.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u043a\u043e\u0434, \u043c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e. \u0410\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u0442\u0435 \u0442\u0430\u0437\u0438 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e, \u043c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0447\u0430\u0441\u043e\u0432\u043d\u0438\u043a\u044a\u0442 \u043d\u0430 Home Assistant \u0435 \u0441\u0432\u0435\u0440\u0435\u043d." + }, + "step": { + "init": { + "description": "\u0417\u0430 \u0434\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u0432\u043e-\u0431\u0430\u0437\u0438\u0440\u0430\u043d\u0438 \u0435\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u0438 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u0439\u0442\u0435 QR \u043a\u043e\u0434\u0430 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0410\u043a\u043e \u043d\u044f\u043c\u0430\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0412\u0438 \u043f\u0440\u0435\u043f\u043e\u0440\u044a\u0447\u0432\u0430\u043c\u0435 \u0438\u043b\u0438 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u0438\u043b\u0438 [Authy](https://authy.com/).\n\n{qr_code}\n\n\u0421\u043b\u0435\u0434 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434\u0430, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 6-\u0442\u0435 \u0446\u0438\u0444\u0440\u0438 \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0437\u0430 \u0434\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0438\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e. \u0410\u043a\u043e \u0438\u043c\u0430\u0442\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u043f\u0440\u0438 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 QR \u043a\u043e\u0434\u0430, \u043d\u0430\u043f\u0440\u0430\u0432\u0435\u0442\u0435 \u0440\u044a\u0447\u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441 \u043a\u043e\u0434 **`{code}`**.", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 \u0434\u0432\u0443\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0440\u0435\u0437 TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/.translations/th.json b/homeassistant/components/cast/.translations/th.json new file mode 100644 index 00000000000000..f0b06a06dc9a36 --- /dev/null +++ b/homeassistant/components/cast/.translations/th.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 Google Cast \u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/daikin/.translations/bg.json b/homeassistant/components/daikin/.translations/bg.json new file mode 100644 index 00000000000000..beb1bc0d6e696b --- /dev/null +++ b/homeassistant/components/daikin/.translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "\u0410\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/daikin/.translations/es-419.json b/homeassistant/components/daikin/.translations/es-419.json index dae3afdba6fab5..6fa2b664a3016c 100644 --- a/homeassistant/components/daikin/.translations/es-419.json +++ b/homeassistant/components/daikin/.translations/es-419.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "device_fail": "Error inesperado al crear el dispositivo.", "device_timeout": "Tiempo de espera de conexi\u00f3n al dispositivo." }, "step": { diff --git a/homeassistant/components/deconz/.translations/bg.json b/homeassistant/components/deconz/.translations/bg.json index 2ea6576206375d..c2cc8f97a8901d 100644 --- a/homeassistant/components/deconz/.translations/bg.json +++ b/homeassistant/components/deconz/.translations/bg.json @@ -11,7 +11,7 @@ "step": { "init": { "data": { - "host": "\u0425\u043e\u0441\u0442", + "host": "\u0410\u0434\u0440\u0435\u0441", "port": "\u041f\u043e\u0440\u0442 (\u0441\u0442\u043e\u0439\u043d\u043e\u0441\u0442 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: '80')" }, "title": "\u0414\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 deCONZ \u0448\u043b\u044e\u0437" diff --git a/homeassistant/components/deconz/.translations/fr.json b/homeassistant/components/deconz/.translations/fr.json index 56399a3c6fd957..d18df13701e97d 100644 --- a/homeassistant/components/deconz/.translations/fr.json +++ b/homeassistant/components/deconz/.translations/fr.json @@ -12,7 +12,7 @@ "init": { "data": { "host": "H\u00f4te", - "port": "Port (valeur par d\u00e9faut : 80)" + "port": "Port" }, "title": "Initialiser la passerelle deCONZ" }, diff --git a/homeassistant/components/deconz/.translations/th.json b/homeassistant/components/deconz/.translations/th.json new file mode 100644 index 00000000000000..e40765e8220a62 --- /dev/null +++ b/homeassistant/components/deconz/.translations/th.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "init": { + "data": { + "port": "Port" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dialogflow/.translations/bg.json b/homeassistant/components/dialogflow/.translations/bg.json new file mode 100644 index 00000000000000..6f06d5c00c628f --- /dev/null +++ b/homeassistant/components/dialogflow/.translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dialogflow/.translations/pl.json b/homeassistant/components/dialogflow/.translations/pl.json index 9a8e1c1eb11759..3395b31b4c79ed 100644 --- a/homeassistant/components/dialogflow/.translations/pl.json +++ b/homeassistant/components/dialogflow/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Dialogflow Webhook]({twilio_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/json\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) by pozna\u0107 szczeg\u00f3\u0142y." + "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Dialogflow Webhook]({twilio_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/json\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}), by pozna\u0107 szczeg\u00f3\u0142y." }, "step": { "user": { diff --git a/homeassistant/components/ebusd/.translations/bg.json b/homeassistant/components/ebusd/.translations/bg.json new file mode 100644 index 00000000000000..f188fe09a48f9f --- /dev/null +++ b/homeassistant/components/ebusd/.translations/bg.json @@ -0,0 +1,6 @@ +{ + "state": { + "day": "\u0414\u0435\u043d", + "night": "\u041d\u043e\u0449" + } +} \ No newline at end of file diff --git a/homeassistant/components/ebusd/.translations/fr.json b/homeassistant/components/ebusd/.translations/fr.json new file mode 100644 index 00000000000000..66a79f926a3dc1 --- /dev/null +++ b/homeassistant/components/ebusd/.translations/fr.json @@ -0,0 +1,6 @@ +{ + "state": { + "day": "journ\u00e9e", + "night": "Nuit" + } +} \ No newline at end of file diff --git a/homeassistant/components/emulated_roku/.translations/bg.json b/homeassistant/components/emulated_roku/.translations/bg.json new file mode 100644 index 00000000000000..77a96095c25ee5 --- /dev/null +++ b/homeassistant/components/emulated_roku/.translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host_ip": "\u0410\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/emulated_roku/.translations/fr.json b/homeassistant/components/emulated_roku/.translations/fr.json index 5da2d437a3586f..629e006564be49 100644 --- a/homeassistant/components/emulated_roku/.translations/fr.json +++ b/homeassistant/components/emulated_roku/.translations/fr.json @@ -1,7 +1,18 @@ { "config": { + "abort": { + "name_exists": "Ce nom est d\u00e9j\u00e0 utilis\u00e9" + }, "step": { "user": { + "data": { + "advertise_ip": "IP d'annonce", + "advertise_port": "Port d'annonce", + "host_ip": "IP h\u00f4te", + "listen_port": "Port d'\u00e9coute", + "name": "Nom", + "upnp_bind_multicast": "Lier la multidiffusion (True / False)" + }, "title": "D\u00e9finir la configuration du serveur" } }, diff --git a/homeassistant/components/esphome/.translations/bg.json b/homeassistant/components/esphome/.translations/bg.json new file mode 100644 index 00000000000000..3574965cae61c9 --- /dev/null +++ b/homeassistant/components/esphome/.translations/bg.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "ESP \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" + }, + "error": { + "connection_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 ESP. \u041c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0432\u0430\u0448\u0438\u044f\u0442 YAML \u0444\u0430\u0439\u043b \u0441\u044a\u0434\u044a\u0440\u0436\u0430 \u0440\u0435\u0434 \"api:\".", + "invalid_password": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u043f\u0430\u0440\u043e\u043b\u0430!", + "resolve_error": "\u041d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0442\u043a\u0440\u0438\u0435 \u0430\u0434\u0440\u0435\u0441\u044a\u0442 \u043d\u0430 ESP. \u0410\u043a\u043e \u0442\u0430\u0437\u0438 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0430\u0432\u0430, \u0437\u0430\u0434\u0430\u0439\u0442\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips" + }, + "step": { + "authenticate": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + }, + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u0430\u0442\u0430, \u043a\u043e\u044f\u0442\u043e \u0441\u0442\u0435 \u0437\u0430\u0434\u0430\u043b\u0438 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0441\u0438 \u0437\u0430 {name} .", + "title": "\u041f\u0430\u0440\u043e\u043b\u0430" + }, + "discovery_confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 ESPHome \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ` {name} ` \u043a\u044a\u043c Home Assistant?", + "title": "\u041e\u0442\u043a\u0440\u0438\u0442\u043e \u0435 ESPHome \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "user": { + "data": { + "host": "\u0410\u0434\u0440\u0435\u0441", + "port": "\u041f\u043e\u0440\u0442" + }, + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u0442\u0435 \u0437\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u0441 [ESPHome](https://esphomelib.com/).", + "title": "ESPHome" + } + }, + "title": "ESPHome" + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/.translations/es-419.json b/homeassistant/components/esphome/.translations/es-419.json index 84000783435f1d..58dbba34fa8384 100644 --- a/homeassistant/components/esphome/.translations/es-419.json +++ b/homeassistant/components/esphome/.translations/es-419.json @@ -16,6 +16,9 @@ "description": "Por favor ingrese la contrase\u00f1a que estableci\u00f3 en su configuraci\u00f3n para {name} .", "title": "Escriba la contrase\u00f1a" }, + "discovery_confirm": { + "title": "Nodo ESPHome descubierto" + }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/esphome/.translations/fr.json b/homeassistant/components/esphome/.translations/fr.json index a52f6159797d98..b230a73c354eda 100644 --- a/homeassistant/components/esphome/.translations/fr.json +++ b/homeassistant/components/esphome/.translations/fr.json @@ -13,7 +13,7 @@ "data": { "password": "Mot de passe" }, - "description": "Veuillez saisir le mot de passe que vous avez d\u00e9fini dans votre configuration.", + "description": "Veuillez saisir le mot de passe que vous avez d\u00e9fini dans votre configuration pour {name}", "title": "Entrer votre mot de passe" }, "discovery_confirm": { diff --git a/homeassistant/components/esphome/.translations/pl.json b/homeassistant/components/esphome/.translations/pl.json index 697fbf0311e042..d24fb929068c1c 100644 --- a/homeassistant/components/esphome/.translations/pl.json +++ b/homeassistant/components/esphome/.translations/pl.json @@ -17,7 +17,7 @@ "title": "Wprowad\u017a has\u0142o" }, "discovery_confirm": { - "description": "Czy chcesz doda\u0107 w\u0119ze\u0142 ESPHome ` {name} ` do Home Assistant?", + "description": "Czy chcesz doda\u0107 w\u0119ze\u0142 ESPHome `{name}` do Home Assistant?", "title": "Znaleziono w\u0119ze\u0142 ESPHome " }, "user": { diff --git a/homeassistant/components/esphome/.translations/uk.json b/homeassistant/components/esphome/.translations/uk.json index 94dafeb3c2e5c8..4f4c2f32c6157f 100644 --- a/homeassistant/components/esphome/.translations/uk.json +++ b/homeassistant/components/esphome/.translations/uk.json @@ -16,6 +16,10 @@ "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c, \u044f\u043a\u0438\u0439 \u0432\u0438 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u0443 \u0441\u0432\u043e\u0457\u0439 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u0457.", "title": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c" }, + "discovery_confirm": { + "description": "\u0414\u043e\u0434\u0430\u0442\u0438 ESPHome \u0432\u0443\u0437\u043e\u043b {name} \u0443 Home Assistant?", + "title": "\u0412\u0438\u044f\u0432\u043b\u0435\u043d\u043e \u0432\u0443\u0437\u043e\u043b ESPHome " + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", diff --git a/homeassistant/components/geofency/.translations/bg.json b/homeassistant/components/geofency/.translations/bg.json new file mode 100644 index 00000000000000..6f06d5c00c628f --- /dev/null +++ b/homeassistant/components/geofency/.translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geofency/.translations/fr.json b/homeassistant/components/geofency/.translations/fr.json index 142f40754b9ddd..b390f2dab44bdd 100644 --- a/homeassistant/components/geofency/.translations/fr.json +++ b/homeassistant/components/geofency/.translations/fr.json @@ -12,6 +12,7 @@ "description": "\u00cates-vous s\u00fbr de vouloir configurer le Webhook Geofency ?", "title": "Configurer le Webhook Geofency" } - } + }, + "title": "Geofency Webhook" } } \ No newline at end of file diff --git a/homeassistant/components/geofency/.translations/pl.json b/homeassistant/components/geofency/.translations/pl.json index 09d93e6911e5d2..b2b8b606723fd1 100644 --- a/homeassistant/components/geofency/.translations/pl.json +++ b/homeassistant/components/geofency/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 webhook w Geofency. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) by pozna\u0107 szczeg\u00f3\u0142y." + "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 webhook w Geofency. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}), by pozna\u0107 szczeg\u00f3\u0142y." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/.translations/bg.json b/homeassistant/components/gpslogger/.translations/bg.json new file mode 100644 index 00000000000000..6f06d5c00c628f --- /dev/null +++ b/homeassistant/components/gpslogger/.translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gpslogger/.translations/pl.json b/homeassistant/components/gpslogger/.translations/pl.json index 3d82ac6fa5a3c8..726ec2ad9b2784 100644 --- a/homeassistant/components/gpslogger/.translations/pl.json +++ b/homeassistant/components/gpslogger/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 lokalizacje do Home Assistant'a, musisz skonfigurowa\u0107 webhook w aplikacji GPSLogger. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) by pozna\u0107 szczeg\u00f3\u0142y." + "default": "Aby wysy\u0142a\u0107 lokalizacje do Home Assistant'a, musisz skonfigurowa\u0107 webhook w aplikacji GPSLogger. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}), by pozna\u0107 szczeg\u00f3\u0142y." }, "step": { "user": { diff --git a/homeassistant/components/hangouts/.translations/th.json b/homeassistant/components/hangouts/.translations/th.json index ae7fc861b77c61..7b6645edca227a 100644 --- a/homeassistant/components/hangouts/.translations/th.json +++ b/homeassistant/components/hangouts/.translations/th.json @@ -1,6 +1,9 @@ { "config": { "step": { + "2fa": { + "title": "\u0e23\u0e2b\u0e31\u0e2a\u0e23\u0e31\u0e1a\u0e23\u0e2d\u0e07\u0e04\u0e27\u0e32\u0e21\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07\u0e2a\u0e2d\u0e07\u0e1b\u0e31\u0e08\u0e08\u0e31\u0e22" + }, "user": { "data": { "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19" diff --git a/homeassistant/components/homekit_controller/.translations/bg.json b/homeassistant/components/homekit_controller/.translations/bg.json new file mode 100644 index 00000000000000..be7d5d323aca31 --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/bg.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0435 \u0432\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e \u0441 \u0442\u043e\u0437\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440.", + "already_paired": "\u0422\u043e\u0437\u0438 \u0430\u043a\u0441\u0435\u0441\u043e\u0430\u0440 \u0432\u0435\u0447\u0435 \u0435 \u0441\u0432\u044a\u0440\u0437\u0430\u043d \u0441 \u0434\u0440\u0443\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e. \u041c\u043e\u043b\u044f, \u0432\u044a\u0437\u0441\u0442\u0430\u043d\u043e\u0432\u0435\u0442\u0435 \u0437\u0430\u0432\u043e\u0434\u0441\u043a\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0438 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "ignored_model": "\u041f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430\u0442\u0430 \u043d\u0430 HomeKit \u0437\u0430 \u0442\u043e\u0437\u0438 \u043c\u043e\u0434\u0435\u043b \u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0430\u043d\u0430, \u0442\u044a\u0439 \u043a\u0430\u0442\u043e \u0435 \u043d\u0430\u043b\u0438\u0446\u0435 \u043f\u043e-\u043f\u044a\u043b\u043d\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0442\u0430.", + "invalid_config_entry": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441\u0435 \u043f\u043e\u043a\u0430\u0437\u0432\u0430 \u043a\u0430\u0442\u043e \u0433\u043e\u0442\u043e\u0432\u043e \u0437\u0430 \u0441\u0434\u0432\u043e\u044f\u0432\u0430\u043d\u0435, \u043d\u043e \u0432\u0435\u0447\u0435 \u0438\u043c\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0437\u0430 \u043d\u0435\u0433\u043e \u0432 Home Assistant, \u043a\u043e\u044f\u0442\u043e \u043f\u044a\u0440\u0432\u043e \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430.", + "no_devices": "\u041d\u0435 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u043d\u0435\u0441\u0432\u044a\u0440\u0437\u0430\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, + "error": { + "authentication_error": "\u0413\u0440\u0435\u0448\u0435\u043d HomeKit \u043a\u043e\u0434. \u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0433\u043e \u0438 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "unable_to_pair": "\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435, \u043c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "unknown_error": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0441\u044a\u043e\u0431\u0449\u0438 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430. \u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u0431\u0435 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "step": { + "pair": { + "data": { + "pairing_code": "\u041a\u043e\u0434 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, + "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 HomeKit \u043a\u043e\u0434\u0430 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0442\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "title": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 HomeKit \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "user": { + "data": { + "device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e, \u0441 \u043a\u043e\u0435\u0442\u043e \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435", + "title": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 HomeKit \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + } + }, + "title": "HomeKit \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/de.json b/homeassistant/components/homekit_controller/.translations/de.json index 1f2dfe66dd24a8..126bb0362a8288 100644 --- a/homeassistant/components/homekit_controller/.translations/de.json +++ b/homeassistant/components/homekit_controller/.translations/de.json @@ -18,7 +18,7 @@ "pairing_code": "Kopplungscode" }, "description": "Geben Sie Ihren HomeKit-Kopplungscode ein, um dieses Zubeh\u00f6r zu verwenden", - "title": "Kopplung mit {{ model }}" + "title": "Mit HomeKit Zubeh\u00f6r koppeln" }, "user": { "data": { diff --git a/homeassistant/components/homekit_controller/.translations/es-419.json b/homeassistant/components/homekit_controller/.translations/es-419.json new file mode 100644 index 00000000000000..fb8d63080ceac5 --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/es-419.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "already_configured": "El accesorio ya est\u00e1 configurado con este controlador.", + "already_paired": "Este accesorio ya est\u00e1 emparejado con otro dispositivo. Por favor, reinicie el accesorio y vuelva a intentarlo." + }, + "step": { + "user": { + "data": { + "device": "Dispositivo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/fr.json b/homeassistant/components/homekit_controller/.translations/fr.json new file mode 100644 index 00000000000000..73cbbdf046ad2a --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/fr.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "L'accessoire est d\u00e9j\u00e0 configur\u00e9 avec ce contr\u00f4leur.", + "already_paired": "Cet accessoire est d\u00e9j\u00e0 associ\u00e9 \u00e0 un autre appareil. R\u00e9initialisez l\u2019accessoire et r\u00e9essayez.", + "ignored_model": "La prise en charge de HomeKit pour ce mod\u00e8le est bloqu\u00e9e car une int\u00e9gration native plus compl\u00e8te est disponible.", + "invalid_config_entry": "Cet appareil est pr\u00eat \u00e0 \u00eatre coupl\u00e9, mais il existe d\u00e9j\u00e0 une entr\u00e9e de configuration en conflit dans Home Assistant \u00e0 supprimer.", + "no_devices": "Aucun appareil non appair\u00e9 n'a pu \u00eatre trouv\u00e9" + }, + "error": { + "authentication_error": "Code HomeKit incorrect. S'il vous pla\u00eet v\u00e9rifier et essayez \u00e0 nouveau.", + "unable_to_pair": "Impossible d'appairer, veuillez r\u00e9essayer.", + "unknown_error": "L'appareil a signal\u00e9 une erreur inconnue. L'appairage a \u00e9chou\u00e9." + }, + "step": { + "pair": { + "data": { + "pairing_code": "Code d\u2019appairage" + }, + "description": "Entrez votre code de jumelage HomeKit pour utiliser cet accessoire.", + "title": "Appairer avec l'accessoire HomeKit" + }, + "user": { + "data": { + "device": "Appareil" + }, + "description": "S\u00e9lectionnez l'appareil avec lequel vous voulez appairer", + "title": "Appairer avec l'accessoire HomeKit" + } + }, + "title": "Accessoire HomeKit" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/th.json b/homeassistant/components/homematicip_cloud/.translations/th.json new file mode 100644 index 00000000000000..cae3361a6ec086 --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/th.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\u0e08\u0e38\u0e14\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e15\u0e48\u0e2d (AP) \u0e44\u0e14\u0e49\u0e17\u0e33\u0e01\u0e32\u0e23\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e04\u0e48\u0e32\u0e41\u0e25\u0e49\u0e27" + }, + "step": { + "init": { + "data": { + "hapid": "\u0e44\u0e2d\u0e14\u0e35\u0e08\u0e38\u0e14\u0e40\u0e02\u0e49\u0e32\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19 (SGTIN)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/.translations/bg.json b/homeassistant/components/hue/.translations/bg.json index 276f5053bf7b75..6a828282f52fed 100644 --- a/homeassistant/components/hue/.translations/bg.json +++ b/homeassistant/components/hue/.translations/bg.json @@ -15,7 +15,7 @@ "step": { "init": { "data": { - "host": "\u0425\u043e\u0441\u0442" + "host": "\u0410\u0434\u0440\u0435\u0441" }, "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0431\u0430\u0437\u043e\u0432\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f Philips Hue" }, diff --git a/homeassistant/components/hue/.translations/th.json b/homeassistant/components/hue/.translations/th.json new file mode 100644 index 00000000000000..0b91783f804390 --- /dev/null +++ b/homeassistant/components/hue/.translations/th.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Philips Hue" + } +} \ No newline at end of file diff --git a/homeassistant/components/ifttt/.translations/bg.json b/homeassistant/components/ifttt/.translations/bg.json new file mode 100644 index 00000000000000..d0fb2a5a04e05d --- /dev/null +++ b/homeassistant/components/ifttt/.translations/bg.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "not_internet_accessible": "Home Assistant \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0435 \u0434\u043e\u0441\u0442\u044a\u043f\u0435\u043d \u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0437\u0430 \u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430 \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 IFTTT.", + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "create_entry": { + "default": "\u0417\u0430 \u0434\u0430 \u0438\u0437\u043f\u0440\u0430\u0449\u0430\u0442\u0435 \u0441\u044a\u0431\u0438\u0442\u0438\u044f \u0432 Home Assistant, \u0449\u0435 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \"Make a web request\" \u043e\u0442 [IFTTT Webhook \u0430\u043f\u043b\u0435\u0442]({applet_url}). \n\n\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0441\u043b\u0435\u0434\u043d\u0430\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f: \n\n - URL: `{webhook_url}` \n - Method: POST \n - Content Type: application/json\n\n \u0412\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430]({docs_url}) \u0437\u0430 \u0442\u043e\u0432\u0430 \u043a\u0430\u043a \u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438\u0442\u0435 \u0437\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438 \u0434\u0430\u043d\u043d\u0438." + }, + "step": { + "user": { + "description": "\u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 IFTTT?", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 IFTTT Webhook \u0430\u043f\u043b\u0435\u0442" + } + }, + "title": "IFTTT" + } +} \ No newline at end of file diff --git a/homeassistant/components/ifttt/.translations/pl.json b/homeassistant/components/ifttt/.translations/pl.json index 7064364ebe6c75..270e74945a3e10 100644 --- a/homeassistant/components/ifttt/.translations/pl.json +++ b/homeassistant/components/ifttt/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wys\u0142a\u0107 zdarzenia do Home Assistant'a, b\u0119dziesz musia\u0142 u\u017cy\u0107 akcji \"Make a web request\" z [IFTTT Webhook apletu]({applet_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}`\n - Metoda: POST\n - Typ zawarto\u015bci: application/json\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." + "default": "Aby wys\u0142a\u0107 zdarzenia do Home Assistant'a, b\u0119dziesz musia\u0142 u\u017cy\u0107 akcji \"Make a web request\" z [IFTTT Webhook apletu]({applet_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}`\n - Metoda: POST\n - Typ zawarto\u015bci: application/json\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji, by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." }, "step": { "user": { diff --git a/homeassistant/components/ios/.translations/bg.json b/homeassistant/components/ios/.translations/bg.json new file mode 100644 index 00000000000000..58028d1caaea5b --- /dev/null +++ b/homeassistant/components/ios/.translations/bg.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 Home Assistant iOS." + }, + "step": { + "confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Home Assistant iOS \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430?", + "title": "Home Assistant iOS" + } + }, + "title": "Home Assistant iOS" + } +} \ No newline at end of file diff --git a/homeassistant/components/ipma/.translations/bg.json b/homeassistant/components/ipma/.translations/bg.json new file mode 100644 index 00000000000000..70d2c6ef6bc1b6 --- /dev/null +++ b/homeassistant/components/ipma/.translations/bg.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "name_exists": "\u0418\u043c\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430" + }, + "step": { + "user": { + "data": { + "latitude": "\u0428\u0438\u0440\u0438\u043d\u0430", + "longitude": "\u0414\u044a\u043b\u0436\u0438\u043d\u0430", + "name": "\u0418\u043c\u0435" + }, + "description": "Instituto Portugu\u00eas do Mar e Atmosfera", + "title": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435" + } + }, + "title": "\u041f\u043e\u0440\u0442\u0443\u0433\u0430\u043b\u0441\u043a\u0430 \u043c\u0435\u0442\u0435\u043e\u0440\u043e\u043b\u043e\u0433\u0438\u0447\u043d\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 (IPMA)" + } +} \ No newline at end of file diff --git a/homeassistant/components/ipma/.translations/fr.json b/homeassistant/components/ipma/.translations/fr.json new file mode 100644 index 00000000000000..058908db36ba4f --- /dev/null +++ b/homeassistant/components/ipma/.translations/fr.json @@ -0,0 +1,13 @@ +{ + "config": { + "error": { + "name_exists": "Ce nom est d\u00e9j\u00e0 utilis\u00e9" + }, + "step": { + "user": { + "title": "Emplacement" + } + }, + "title": "Service m\u00e9t\u00e9orologique portugais (IPMA)" + } +} \ No newline at end of file diff --git a/homeassistant/components/locative/.translations/bg.json b/homeassistant/components/locative/.translations/bg.json new file mode 100644 index 00000000000000..1e80c86e862617 --- /dev/null +++ b/homeassistant/components/locative/.translations/bg.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "not_internet_accessible": "Home Assistant \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0435 \u0434\u043e\u0441\u0442\u044a\u043f\u0435\u043d \u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0437\u0430 \u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430 \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442 Geofency", + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "create_entry": { + "default": "\u0417\u0430 \u0434\u0430 \u0438\u0437\u043f\u0440\u0430\u0449\u0430\u0442\u0435 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u044a\u043c Home Assistant, \u0449\u0435 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0442\u0430 webhook \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e Locative. \n\n \u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0441\u043b\u0435\u0434\u043d\u0430\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f: \n\n - URL: ` {webhook_url} ` \n - \u041c\u0435\u0442\u043e\u0434: POST \n\n \u0412\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430]({docs_url}) \u0437\u0430 \u043f\u043e\u0432\u0435\u0447\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438." + }, + "step": { + "user": { + "description": "\u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Locative Webhook?", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435 \u043d\u0430 Locative Webhook" + } + }, + "title": "Locative Webhook" + } +} \ No newline at end of file diff --git a/homeassistant/components/locative/.translations/fr.json b/homeassistant/components/locative/.translations/fr.json index 81950c49b4c20e..a90f7ff989c896 100644 --- a/homeassistant/components/locative/.translations/fr.json +++ b/homeassistant/components/locative/.translations/fr.json @@ -3,6 +3,16 @@ "abort": { "not_internet_accessible": "Votre instance Home Assistant doit \u00eatre accessible \u00e0 partir d'Internet pour recevoir les messages Geofency.", "one_instance_allowed": "Une seule instance est n\u00e9cessaire." - } + }, + "create_entry": { + "default": "Pour envoyer des localisations \u00e0 Home Assistant, vous devez configurer la fonctionnalit\u00e9 Webhook dans l'application Locative. \n\n Remplissez les informations suivantes: \n\n - URL: ` {webhook_url} ` \n - M\u00e9thode: POST \n\n Voir [la documentation] ( {docs_url} ) pour plus de d\u00e9tails." + }, + "step": { + "user": { + "description": "\u00cates-vous s\u00fbr de vouloir configurer le Webhook Locative ?", + "title": "Configurer le Locative Webhook" + } + }, + "title": "Locative Webhook" } } \ No newline at end of file diff --git a/homeassistant/components/locative/.translations/pl.json b/homeassistant/components/locative/.translations/pl.json index 89f6881593aa06..917744c32fd2ba 100644 --- a/homeassistant/components/locative/.translations/pl.json +++ b/homeassistant/components/locative/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 lokalizacje do Home Assistant'a, musisz skonfigurowa\u0107 webhook w aplikacji Locative. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) by pozna\u0107 szczeg\u00f3\u0142y." + "default": "Aby wysy\u0142a\u0107 lokalizacje do Home Assistant'a, musisz skonfigurowa\u0107 webhook w aplikacji Locative. \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}), by pozna\u0107 szczeg\u00f3\u0142y." }, "step": { "user": { diff --git a/homeassistant/components/luftdaten/.translations/bg.json b/homeassistant/components/luftdaten/.translations/bg.json new file mode 100644 index 00000000000000..ecd7f17c84be41 --- /dev/null +++ b/homeassistant/components/luftdaten/.translations/bg.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "communication_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430 \u043a\u043e\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u044f \u0441 Luftdaten", + "invalid_sensor": "\u0421\u0435\u043d\u0437\u043e\u0440\u044a\u0442 \u043d\u0435 \u0435 \u043d\u0430\u043b\u0438\u0447\u0435\u043d \u0438\u043b\u0438 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d", + "sensor_exists": "\u0421\u0435\u043d\u0437\u043e\u0440\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d" + }, + "step": { + "user": { + "data": { + "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u0440\u0442\u0430\u0442\u0430", + "station_id": "ID \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440\u0430 \u043d\u0430 Luftdaten" + }, + "title": "\u0414\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 Luftdaten" + } + }, + "title": "Luftdaten" + } +} \ No newline at end of file diff --git a/homeassistant/components/luftdaten/.translations/fr.json b/homeassistant/components/luftdaten/.translations/fr.json new file mode 100644 index 00000000000000..2aeb29fcf0ab32 --- /dev/null +++ b/homeassistant/components/luftdaten/.translations/fr.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "communication_error": "Impossible de communiquer avec l'API Luftdaten", + "invalid_sensor": "Capteur non disponible ou invalide", + "sensor_exists": "Capteur d\u00e9j\u00e0 enregistr\u00e9" + }, + "step": { + "user": { + "data": { + "show_on_map": "Montrer sur la carte" + }, + "title": "D\u00e9finir Luftdaten" + } + }, + "title": "Luftdaten" + } +} \ No newline at end of file diff --git a/homeassistant/components/mailgun/.translations/bg.json b/homeassistant/components/mailgun/.translations/bg.json new file mode 100644 index 00000000000000..6f06d5c00c628f --- /dev/null +++ b/homeassistant/components/mailgun/.translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mailgun/.translations/pl.json b/homeassistant/components/mailgun/.translations/pl.json index ba89efab0c270e..ccdc368afffd4e 100644 --- a/homeassistant/components/mailgun/.translations/pl.json +++ b/homeassistant/components/mailgun/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Mailgun Webhook]({mailgun_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/x-www-form-urlencoded \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." + "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Mailgun Webhook]({mailgun_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/x-www-form-urlencoded \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji, by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." }, "step": { "user": { diff --git a/homeassistant/components/mobile_app/.translations/ca.json b/homeassistant/components/mobile_app/.translations/ca.json new file mode 100644 index 00000000000000..25af1d5e18d8de --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/ca.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Obre l\u2019aplicaci\u00f3 m\u00f2bil per configurar la integraci\u00f3 amb Home Assistant. Mira [la documentaci\u00f3]({apps_url}) per veure la llista d\u2019aplicacions compatibles." + }, + "step": { + "confirm": { + "description": "Vols configurar el component d'aplicaci\u00f3 m\u00f2bil?", + "title": "Aplicaci\u00f3 m\u00f2bil" + } + }, + "title": "Aplicaci\u00f3 m\u00f2bil" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/de.json b/homeassistant/components/mobile_app/.translations/de.json new file mode 100644 index 00000000000000..816d281752db4a --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/de.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "\u00d6ffne die mobile App, um die Integration mit Home Assistant einzurichten. Eine Liste der kompatiblen Apps gibt es hier [the docs] ({apps_url})." + }, + "step": { + "confirm": { + "description": "M\u00f6chtest du die Mobile App-Komponente einrichten?", + "title": "Mobile App" + } + }, + "title": "Mobile App" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/en.json b/homeassistant/components/mobile_app/.translations/en.json index 646151a522909a..79a5fe1fba8e7f 100644 --- a/homeassistant/components/mobile_app/.translations/en.json +++ b/homeassistant/components/mobile_app/.translations/en.json @@ -1,14 +1,14 @@ { - "config": { - "title": "Mobile App", - "step": { - "confirm": { - "title": "Mobile App", - "description": "Do you want to set up the Mobile App component?" - } - }, - "abort": { - "install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps." + "config": { + "abort": { + "install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps." + }, + "step": { + "confirm": { + "description": "Do you want to set up the Mobile App component?", + "title": "Mobile App" + } + }, + "title": "Mobile App" } - } -} +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/es-419.json b/homeassistant/components/mobile_app/.translations/es-419.json new file mode 100644 index 00000000000000..417d0627616093 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/es-419.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "Aplicaci\u00f3n movil" + } + }, + "title": "Aplicaci\u00f3n movil" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/ko.json b/homeassistant/components/mobile_app/.translations/ko.json new file mode 100644 index 00000000000000..faf30e5f985ea2 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "\ubaa8\ubc14\uc77c \uc571\uc744 \uc5f4\uc5b4 Home Assistant \uc640 \ud1b5\ud569\uc744 \uc124\uc815\ud574\uc8fc\uc138\uc694. \ud638\ud658\ub418\ub294 \uc571 \ubaa9\ub85d\uc740 [\uc548\ub0b4]({apps_url}) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694." + }, + "step": { + "confirm": { + "description": "\ubaa8\ubc14\uc77c \uc571 \ucef4\ud3ec\ub10c\ud2b8\uc758 \uc124\uc815\uc744 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", + "title": "\ubaa8\ubc14\uc77c \uc571" + } + }, + "title": "\ubaa8\ubc14\uc77c \uc571" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/lb.json b/homeassistant/components/mobile_app/.translations/lb.json new file mode 100644 index 00000000000000..a66ae603291ad2 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/lb.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Maacht d'Mobil App op fir d'Integratioun mam Home Assistant anzeriichten. Kuckt an der [Dokumentatioun]({apps_url}) fir eng L\u00ebscht vun kompatiblen App's." + }, + "step": { + "confirm": { + "description": "Soll d'Mobil App konfigur\u00e9iert ginn?", + "title": "Mobil App" + } + }, + "title": "Mobil App" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/no.json b/homeassistant/components/mobile_app/.translations/no.json new file mode 100644 index 00000000000000..7189bc53c1699c --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/no.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "\u00c5pne mobilappen for \u00e5 konfigurere integrasjonen med hjemmevirksomheten. Se [docs]({apps_url}) for en liste over kompatible apper." + }, + "step": { + "confirm": { + "description": "Vil du sette opp mobilapp-komponenten?", + "title": "Mobilapp" + } + }, + "title": "Mobilapp" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/pl.json b/homeassistant/components/mobile_app/.translations/pl.json new file mode 100644 index 00000000000000..feb00c20779d3c --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/pl.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Otw\u00f3rz aplikacj\u0119 mobiln\u0105, aby skonfigurowa\u0107 integracj\u0119 z Home Assistant. Zapoznaj si\u0119 z [dokumentacj\u0105] ({apps_url}), by zobaczy\u0107 list\u0119 kompatybilnych aplikacji." + }, + "step": { + "confirm": { + "description": "Czy chcesz skonfigurowa\u0107 komponent aplikacji mobilnej?", + "title": "Aplikacja mobilna" + } + }, + "title": "Aplikacja mobilna" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/ru.json b/homeassistant/components/mobile_app/.translations/ru.json new file mode 100644 index 00000000000000..202b73832531d9 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/ru.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 Home Assistant. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({apps_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439." + }, + "step": { + "confirm": { + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435?", + "title": "\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435" + } + }, + "title": "\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/uk.json b/homeassistant/components/mobile_app/.translations/uk.json new file mode 100644 index 00000000000000..654eb7675a876d --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/uk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\u0412\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043c\u043e\u0431\u0456\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u043e\u0434\u0430\u0442\u043a\u0430?", + "title": "\u041c\u043e\u0431\u0456\u043b\u044c\u043d\u0438\u0439 \u0434\u043e\u0434\u0430\u0442\u043e\u043a" + } + }, + "title": "\u041c\u043e\u0431\u0456\u043b\u044c\u043d\u0438\u0439 \u0434\u043e\u0434\u0430\u0442\u043e\u043a" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/zh-Hant.json b/homeassistant/components/mobile_app/.translations/zh-Hant.json new file mode 100644 index 00000000000000..3b1ab72f7d317a --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/zh-Hant.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "\u958b\u555f\u624b\u6a5f App \u4ee5\u9032\u884c Home Assistant \u6574\u5408\u8a2d\u5b9a\u3002\u8acb\u53c3\u95b1 [\u6587\u4ef6]({apps_url}) \u7372\u5f97\u652f\u63f4\u7684\u624b\u6a5f App \u5217\u8868\u3002" + }, + "step": { + "confirm": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u624b\u6a5f App \u5143\u4ef6\uff1f", + "title": "\u624b\u6a5f App" + } + }, + "title": "\u624b\u6a5f App" + } +} \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.ca.json b/homeassistant/components/moon/.translations/sensor.ca.json index 56eaf8d3b4c8fe..e294579da09164 100644 --- a/homeassistant/components/moon/.translations/sensor.ca.json +++ b/homeassistant/components/moon/.translations/sensor.ca.json @@ -4,7 +4,7 @@ "full_moon": "Lluna plena", "last_quarter": "Quart minvant", "new_moon": "Lluna nova", - "waning_crescent": "Lluna vella minvant", + "waning_crescent": "Minvant (Lluna vella)", "waning_gibbous": "Gibosa minvant", "waxing_crescent": "Lluna nova visible", "waxing_gibbous": "Gibosa creixent" diff --git a/homeassistant/components/moon/.translations/sensor.no.json b/homeassistant/components/moon/.translations/sensor.no.json index 19f9985accb412..a440fdde4f27ca 100644 --- a/homeassistant/components/moon/.translations/sensor.no.json +++ b/homeassistant/components/moon/.translations/sensor.no.json @@ -1,12 +1,12 @@ { "state": { - "first_quarter": "F\u00f8rste kvarter", + "first_quarter": "F\u00f8rste kvartal", "full_moon": "Fullm\u00e5ne", "last_quarter": "Siste kvarter", "new_moon": "Nym\u00e5ne", - "waning_crescent": "Minkende halvm\u00e5ne", - "waning_gibbous": "Minkende trekvartm\u00e5ne", - "waxing_crescent": "Voksende halvm\u00e5ne", - "waxing_gibbous": "Voksende trekvartm\u00e5ne" + "waning_crescent": "Minkende m\u00e5nesigd", + "waning_gibbous": "Minkende m\u00e5ne", + "waxing_crescent": "Voksende m\u00e5nesigd", + "waxing_gibbous": "Voksende m\u00e5ne" } } \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.uk.json b/homeassistant/components/moon/.translations/sensor.uk.json index 2467a705d50a32..4e1a9f7acabc7c 100644 --- a/homeassistant/components/moon/.translations/sensor.uk.json +++ b/homeassistant/components/moon/.translations/sensor.uk.json @@ -3,10 +3,6 @@ "first_quarter": "\u041f\u0435\u0440\u0448\u0430 \u0447\u0432\u0435\u0440\u0442\u044c", "full_moon": "\u041f\u043e\u0432\u043d\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", "last_quarter": "\u041e\u0441\u0442\u0430\u043d\u043d\u044f \u0447\u0432\u0435\u0440\u0442\u044c", - "new_moon": "\u041d\u043e\u0432\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", - "waning_crescent": "\u0417\u0440\u043e\u0441\u0442\u0430\u044e\u0447\u0438\u0439 \u043f\u0456\u0432\u043c\u0456\u0441\u044f\u0446\u044c", - "waning_gibbous": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", - "waxing_crescent": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", - "waxing_gibbous": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c" + "new_moon": "\u041d\u043e\u0432\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c" } } \ No newline at end of file diff --git a/homeassistant/components/mqtt/.translations/bg.json b/homeassistant/components/mqtt/.translations/bg.json new file mode 100644 index 00000000000000..4312bdba6ecb84 --- /dev/null +++ b/homeassistant/components/mqtt/.translations/bg.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 MQTT." + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0431\u0440\u043e\u043a\u0435\u0440\u0430." + }, + "step": { + "broker": { + "data": { + "broker": "\u0411\u0440\u043e\u043a\u0435\u0440", + "discovery": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "port": "\u041f\u043e\u0440\u0442", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + }, + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0437\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0412\u0430\u0448\u0438\u044f MQTT \u0431\u0440\u043e\u043a\u0435\u0440.", + "title": "MQTT" + }, + "hassio_confirm": { + "data": { + "discovery": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442\u0435 Home Assistant \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435 \u0441 MQTT \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0435\u043d \u043e\u0442 hass.io \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 {addon}?", + "title": "MQTT \u0431\u0440\u043e\u043a\u0435\u0440 \u0447\u0440\u0435\u0437 Hass.io \u0434\u043e\u0431\u0430\u0432\u043a\u0430" + } + }, + "title": "MQTT" + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/.translations/es-419.json b/homeassistant/components/nest/.translations/es-419.json index 117a4500d5872f..78239148a4ea5e 100644 --- a/homeassistant/components/nest/.translations/es-419.json +++ b/homeassistant/components/nest/.translations/es-419.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_setup": "Solo puedes configurar una sola cuenta Nest.", + "authorize_url_fail": "Error desconocido al generar una URL de autorizaci\u00f3n.", "no_flows": "Debe configurar Nest antes de poder autenticarse con \u00e9l. [Lea las instrucciones] (https://www.home-assistant.io/components/nest/)." }, "error": { diff --git a/homeassistant/components/owntracks/.translations/bg.json b/homeassistant/components/owntracks/.translations/bg.json new file mode 100644 index 00000000000000..1989e1a0703290 --- /dev/null +++ b/homeassistant/components/owntracks/.translations/bg.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "create_entry": { + "default": "\n\n \u0412 Android \u043e\u0442\u0432\u043e\u0440\u0435\u0442\u0435 [\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e OwnTracks]({android_url}), \u043f\u0440\u0435\u043c\u0438\u043d\u0435\u0442\u0435 \u043a\u044a\u043c Preferences - > Connection. \u041f\u0440\u043e\u043c\u0435\u043d\u0435\u0442\u0435 \u0441\u043b\u0435\u0434\u043d\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438: \n - Mode: Private HTTP\n - Host: {webhook_url} \n - Identification: \n - Username: ` ` \n - Device ID: ` ` \n\n \u0412 iOS \u043e\u0442\u0432\u043e\u0440\u0435\u0442\u0435 [\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e OwnTracks]({ios_url}), \u0434\u043e\u043a\u043e\u0441\u043d\u0435\u0442\u0435 (i) \u0438\u043a\u043e\u043d\u0430\u0442\u0430 \u0432 \u0433\u043e\u0440\u043d\u0438\u044f \u043b\u044f\u0432 \u044a\u0433\u044a\u043b - > Settings. \u041f\u0440\u043e\u043c\u0435\u043d\u0435\u0442\u0435 \u0441\u043b\u0435\u0434\u043d\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438: \n - Mode: HTTP \n - URL: {webhook_url} \n - Turn on authentication \n - UserID: ` ` \n\n {secret} \n \n \u0412\u0438\u0436\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430]({docs_url}) \u0437\u0430 \u043f\u043e\u0432\u0435\u0447\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f." + }, + "step": { + "user": { + "description": "\u0421\u0438\u0433\u0443\u0440\u043d\u0438 \u043b\u0438 \u0441\u0442\u0435, \u0447\u0435 \u0438\u0441\u043a\u0430\u0442\u0435 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 OwnTracks?", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 OwnTracks" + } + }, + "title": "OwnTracks" + } +} \ No newline at end of file diff --git a/homeassistant/components/owntracks/.translations/fr.json b/homeassistant/components/owntracks/.translations/fr.json new file mode 100644 index 00000000000000..46a0f2f2921261 --- /dev/null +++ b/homeassistant/components/owntracks/.translations/fr.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "Une seule instance est n\u00e9cessaire." + }, + "step": { + "user": { + "description": "\u00cates-vous s\u00fbr de vouloir configurer OwnTracks?", + "title": "Configurer OwnTracks" + } + }, + "title": "OwnTracks" + } +} \ No newline at end of file diff --git a/homeassistant/components/owntracks/.translations/pl.json b/homeassistant/components/owntracks/.translations/pl.json index 85a39095f87a9f..fd6cba182374e1 100644 --- a/homeassistant/components/owntracks/.translations/pl.json +++ b/homeassistant/components/owntracks/.translations/pl.json @@ -4,7 +4,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "\n\nNa Androidzie, otw\u00f3rz [aplikacj\u0119 OwnTracks]({android_url}), id\u017a do: ustawienia -> po\u0142aczenia. Zmie\u0144 nast\u0119puj\u0105ce ustawienia:\n - Tryb: Private HTTP\n - Host: {webhook_url}\n - Identyfikacja:\n - Nazwa u\u017cytkownika: ``\n - ID urz\u0105dzenia: ``\n\nNa iOS, otw\u00f3rz [aplikacj\u0119 OwnTracks]({ios_url}), naci\u015bnij ikon\u0119 (i) w lewym g\u00f3rnym rogu -> ustawienia. Zmie\u0144 nast\u0119puj\u0105ce ustawienia:\n - Tryb: HTTP\n - URL: {webhook_url}\n - W\u0142\u0105cz uwierzytelnianie\n - ID u\u017cytkownika: ``\n\n{secret}\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) by pozna\u0107 szczeg\u00f3\u0142y." + "default": "\n\nNa Androidzie, otw\u00f3rz [aplikacj\u0119 OwnTracks]({android_url}), id\u017a do: ustawienia -> po\u0142aczenia. Zmie\u0144 nast\u0119puj\u0105ce ustawienia:\n - Tryb: Private HTTP\n - Host: {webhook_url}\n - Identyfikacja:\n - Nazwa u\u017cytkownika: ``\n - ID urz\u0105dzenia: ``\n\nNa iOS, otw\u00f3rz [aplikacj\u0119 OwnTracks]({ios_url}), naci\u015bnij ikon\u0119 (i) w lewym g\u00f3rnym rogu -> ustawienia. Zmie\u0144 nast\u0119puj\u0105ce ustawienia:\n - Tryb: HTTP\n - URL: {webhook_url}\n - W\u0142\u0105cz uwierzytelnianie\n - ID u\u017cytkownika: ``\n\n{secret}\n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}), by pozna\u0107 szczeg\u00f3\u0142y." }, "step": { "user": { diff --git a/homeassistant/components/point/.translations/fr.json b/homeassistant/components/point/.translations/fr.json new file mode 100644 index 00000000000000..ba1b1e27668235 --- /dev/null +++ b/homeassistant/components/point/.translations/fr.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_setup": "Vous ne pouvez configurer qu'un compte Point.", + "authorize_url_fail": "Erreur inconnue lors de la g\u00e9n\u00e9ration d'une URL d'autorisation.", + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification d\u00e9pass\u00e9." + }, + "step": { + "user": { + "data": { + "flow_impl": "Fournisseur" + }, + "description": "Choisissez via quel fournisseur d'authentification vous souhaitez vous authentifier avec Point.", + "title": "Fournisseur d'authentification" + } + }, + "title": "Minut Point" + } +} \ No newline at end of file diff --git a/homeassistant/components/ps4/.translations/bg.json b/homeassistant/components/ps4/.translations/bg.json new file mode 100644 index 00000000000000..0a3c4393c7c3c8 --- /dev/null +++ b/homeassistant/components/ps4/.translations/bg.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "credential_error": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0438\u0437\u0432\u043b\u0438\u0447\u0430\u043d\u0435 \u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438.", + "devices_configured": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441\u0430 \u0432\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438.", + "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 PlayStation 4 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430.", + "port_987_bind_error": "\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442 \u0437\u0430 \u0440\u0435\u0437\u0435\u0440\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u043e\u0440\u0442 987.", + "port_997_bind_error": "\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442 \u0437\u0430 \u0440\u0435\u0437\u0435\u0440\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u043e\u0440\u0442 997." + }, + "error": { + "login_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 PlayStation 4. \u041f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0434\u0430\u043b\u0438 \u0432\u044a\u0432\u0435\u0434\u0435\u043d\u0438\u044f PIN \u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0435\u043d.", + "not_ready": "PlayStation 4 \u043a\u043e\u043d\u0437\u043e\u043b\u0430\u0442\u0430 \u043d\u0435 \u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0438\u043b\u0438 \u0441\u0432\u044a\u0440\u0437\u0430\u043d\u0430 \u043a\u044a\u043c \u043c\u0440\u0435\u0436\u0430\u0442\u0430." + }, + "step": { + "creds": { + "description": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0438 \u0441\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438. \u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"\u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435\" \u0438 \u0441\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u0432 PS4 2nd Screen App, \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"Refresh devices\" \u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \"Home-Assistant\" \u0437\u0430 \u0434\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435.", + "title": "PlayStation 4" + }, + "link": { + "data": { + "code": "PIN", + "ip_address": "IP \u0430\u0434\u0440\u0435\u0441", + "name": "\u0418\u043c\u0435", + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + }, + "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0432\u0430\u0448\u0430\u0442\u0430 PlayStation 4 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f. \u0417\u0430 \u201ePIN\u201c \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u0432 \u201eSettings\u201c \u043d\u0430 \u0432\u0430\u0448\u0430\u0442\u0430 PlayStation 4 \u043a\u043e\u043d\u0437\u043e\u043b\u0430. \u0421\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u043f\u0440\u0435\u043c\u0438\u043d\u0435\u0442\u0435 \u043a\u044a\u043c \u201eMobile App Connection Settings\u201c \u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u201eAdd Device\u201c. \u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0438\u044f PIN \u043a\u043e\u0434.", + "title": "PlayStation 4" + } + }, + "title": "PlayStation 4" + } +} \ No newline at end of file diff --git a/homeassistant/components/ps4/.translations/fr.json b/homeassistant/components/ps4/.translations/fr.json index d7983448417644..bb654eed22888e 100644 --- a/homeassistant/components/ps4/.translations/fr.json +++ b/homeassistant/components/ps4/.translations/fr.json @@ -1,7 +1,19 @@ { "config": { + "abort": { + "credential_error": "Erreur lors de l'extraction des informations d'identification.", + "devices_configured": "Tous les p\u00e9riph\u00e9riques trouv\u00e9s sont d\u00e9j\u00e0 configur\u00e9s.", + "no_devices_found": "Aucun appareil PlayStation 4 trouv\u00e9 sur le r\u00e9seau.", + "port_987_bind_error": "Impossible de se connecter au port 997.", + "port_997_bind_error": "Impossible de se connecter au port 997." + }, + "error": { + "login_failed": "\u00c9chec de l'association \u00e0 la PlayStation 4. V\u00e9rifiez que le code PIN est correct.", + "not_ready": "PlayStation 4 n'est pas allum\u00e9e ou connect\u00e9e au r\u00e9seau." + }, "step": { "creds": { + "description": "Informations d\u2019identification n\u00e9cessaires. Appuyez sur \u00ab\u00a0Envoyer\u00a0\u00bb puis dans la PS4 2\u00e8me \u00e9cran App, actualisez les p\u00e9riph\u00e9riques et s\u00e9lectionnez le dispositif \u00ab\u00a0Home Assistant\u00a0\u00bb pour continuer.", "title": "PlayStation 4" }, "link": { diff --git a/homeassistant/components/rainmachine/.translations/bg.json b/homeassistant/components/rainmachine/.translations/bg.json new file mode 100644 index 00000000000000..80fb5f07f13fb4 --- /dev/null +++ b/homeassistant/components/rainmachine/.translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "ip_address": "\u0410\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rainmachine/.translations/fr.json b/homeassistant/components/rainmachine/.translations/fr.json new file mode 100644 index 00000000000000..64d8f582ad3bdd --- /dev/null +++ b/homeassistant/components/rainmachine/.translations/fr.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "identifier_exists": "Compte d\u00e9j\u00e0 enregistr\u00e9", + "invalid_credentials": "Informations d'identification invalides" + }, + "step": { + "user": { + "data": { + "ip_address": "Nom d'h\u00f4te ou adresse IP", + "password": "Mot de passe", + "port": "Port" + }, + "title": "Veuillez saisir vos informations" + } + }, + "title": "RainMachine" + } +} \ No newline at end of file diff --git a/homeassistant/components/season/.translations/sensor.pl.json b/homeassistant/components/season/.translations/sensor.pl.json index f5a7da57e7f4b1..9b313e511c9c2b 100644 --- a/homeassistant/components/season/.translations/sensor.pl.json +++ b/homeassistant/components/season/.translations/sensor.pl.json @@ -1,8 +1,8 @@ { "state": { - "autumn": "Jesie\u0144", - "spring": "Wiosna", - "summer": "Lato", - "winter": "Zima" + "autumn": "jesie\u0144", + "spring": "wiosna", + "summer": "lato", + "winter": "zima" } } \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.ar.json b/homeassistant/components/sensor/.translations/moon.ar.json new file mode 100644 index 00000000000000..94af741f5f4de7 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.ar.json @@ -0,0 +1,6 @@ +{ + "state": { + "first_quarter": "\u0627\u0644\u0631\u0628\u0639 \u0627\u0644\u0623\u0648\u0644", + "full_moon": "\u0627\u0644\u0642\u0645\u0631 \u0627\u0644\u0643\u0627\u0645\u0644" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.bg.json b/homeassistant/components/sensor/.translations/moon.bg.json new file mode 100644 index 00000000000000..c764ccbc3e057e --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.bg.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u041f\u044a\u0440\u0432\u0430 \u0447\u0435\u0442\u0432\u044a\u0440\u0442\u0438\u043d\u0430", + "full_moon": "\u041f\u044a\u043b\u043d\u043e\u043b\u0443\u043d\u0438\u0435", + "last_quarter": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0430 \u0447\u0435\u0442\u0432\u044a\u0440\u0442\u0438\u043d\u0430", + "new_moon": "\u041d\u043e\u0432\u043e\u043b\u0443\u043d\u0438\u0435", + "waning_crescent": "\u041d\u0430\u043c\u0430\u043b\u044f\u0432\u0430\u0449 \u043f\u043e\u043b\u0443\u043c\u0435\u0441\u0435\u0446", + "waning_gibbous": "\u041d\u0430\u043c\u0430\u043b\u044f\u0432\u0430\u0449 \u043f\u043e\u043b\u0443\u043c\u0435\u0441\u0435\u0446", + "waxing_crescent": "\u041d\u0430\u0440\u0430\u0441\u0442\u0432\u0430\u0449 \u043f\u043e\u043b\u0443\u043c\u0435\u0441\u0435\u0446", + "waxing_gibbous": "\u041d\u0430\u0440\u0430\u0441\u0442\u0432\u0430\u0449 \u043f\u043e\u043b\u0443\u043c\u0435\u0441\u0435\u0446" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.ca.json b/homeassistant/components/sensor/.translations/moon.ca.json new file mode 100644 index 00000000000000..e294579da09164 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.ca.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Quart creixent", + "full_moon": "Lluna plena", + "last_quarter": "Quart minvant", + "new_moon": "Lluna nova", + "waning_crescent": "Minvant (Lluna vella)", + "waning_gibbous": "Gibosa minvant", + "waxing_crescent": "Lluna nova visible", + "waxing_gibbous": "Gibosa creixent" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.cs.json b/homeassistant/components/sensor/.translations/moon.cs.json new file mode 100644 index 00000000000000..ef1d5bf5f132fe --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.cs.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Prvn\u00ed \u010dtvr\u0165", + "full_moon": "\u00dapln\u011bk", + "last_quarter": "Posledn\u00ed \u010dtvr\u0165", + "new_moon": "Nov", + "waning_crescent": "Couvaj\u00edc\u00ed srpek", + "waning_gibbous": "Couvaj\u00edc\u00ed m\u011bs\u00edc", + "waxing_crescent": "Dor\u016fstaj\u00edc\u00ed srpek", + "waxing_gibbous": "Dor\u016fstaj\u00edc\u00ed m\u011bs\u00edc" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.da.json b/homeassistant/components/sensor/.translations/moon.da.json new file mode 100644 index 00000000000000..c2406de68bbfa1 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.da.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "F\u00f8rste kvartal", + "full_moon": "Fuldm\u00e5ne", + "last_quarter": "Sidste kvartal", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Aftagende halvm\u00e5ne", + "waning_gibbous": "Aftagende m\u00e5ne", + "waxing_crescent": "Tiltagende halvm\u00e5ne", + "waxing_gibbous": "Tiltagende m\u00e5ne" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.de.json b/homeassistant/components/sensor/.translations/moon.de.json new file mode 100644 index 00000000000000..310ebf9c3592ba --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.de.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Erstes Viertel", + "full_moon": "Vollmond", + "last_quarter": "Letztes Viertel", + "new_moon": "Neumond", + "waning_crescent": "Abnehmende Sichel", + "waning_gibbous": "Drittes Viertel", + "waxing_crescent": "Zunehmende Sichel", + "waxing_gibbous": "Zweites Viertel" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.en.json b/homeassistant/components/sensor/.translations/moon.en.json new file mode 100644 index 00000000000000..587b9496114118 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.en.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "First quarter", + "full_moon": "Full moon", + "last_quarter": "Last quarter", + "new_moon": "New moon", + "waning_crescent": "Waning crescent", + "waning_gibbous": "Waning gibbous", + "waxing_crescent": "Waxing crescent", + "waxing_gibbous": "Waxing gibbous" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.es-419.json b/homeassistant/components/sensor/.translations/moon.es-419.json new file mode 100644 index 00000000000000..71cfab736cb6be --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.es-419.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Cuarto creciente", + "full_moon": "Luna llena", + "last_quarter": "Cuarto menguante", + "new_moon": "Luna nueva", + "waning_crescent": "Luna menguante", + "waning_gibbous": "Luna menguante gibosa", + "waxing_crescent": "Luna creciente", + "waxing_gibbous": "Luna creciente gibosa" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.es.json b/homeassistant/components/sensor/.translations/moon.es.json new file mode 100644 index 00000000000000..3ce14cd4c770ea --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.es.json @@ -0,0 +1,10 @@ +{ + "state": { + "first_quarter": "Primer cuarto", + "full_moon": "Luna llena", + "last_quarter": "\u00daltimo cuarto", + "new_moon": "Luna nueva", + "waning_crescent": "Luna menguante", + "waxing_crescent": "Luna creciente" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.et.json b/homeassistant/components/sensor/.translations/moon.et.json new file mode 100644 index 00000000000000..0d82e0d8f94edf --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.et.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Esimene veerand", + "full_moon": "T\u00e4iskuu", + "last_quarter": "Viimane veerand", + "new_moon": "Kuu loomine", + "waning_crescent": "Vanakuu", + "waning_gibbous": "Kahanev kuu", + "waxing_crescent": "Noorkuu", + "waxing_gibbous": "Kasvav kuu" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.fi.json b/homeassistant/components/sensor/.translations/moon.fi.json new file mode 100644 index 00000000000000..10f8bb9b8a67c8 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.fi.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Ensimm\u00e4inen nelj\u00e4nnes", + "full_moon": "T\u00e4ysikuu", + "last_quarter": "Viimeinen nelj\u00e4nnes", + "new_moon": "Uusikuu", + "waning_crescent": "V\u00e4henev\u00e4 sirppi", + "waning_gibbous": "V\u00e4henev\u00e4 kuperakuu", + "waxing_crescent": "Kasvava sirppi", + "waxing_gibbous": "Kasvava kuperakuu" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.fr.json b/homeassistant/components/sensor/.translations/moon.fr.json new file mode 100644 index 00000000000000..fac2b654a4664d --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.fr.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Premier quartier", + "full_moon": "Pleine lune", + "last_quarter": "Dernier quartier", + "new_moon": "Nouvelle lune", + "waning_crescent": "Dernier croissant", + "waning_gibbous": "Gibbeuse d\u00e9croissante", + "waxing_crescent": "Premier croissant", + "waxing_gibbous": "Gibbeuse croissante" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.he.json b/homeassistant/components/sensor/.translations/moon.he.json new file mode 100644 index 00000000000000..6531d3c82657cc --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.he.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05e8\u05d0\u05e9\u05d5\u05df", + "full_moon": "\u05d9\u05e8\u05d7 \u05de\u05dc\u05d0", + "last_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05d0\u05d7\u05e8\u05d5\u05df", + "new_moon": "\u05e8\u05d0\u05e9 \u05d7\u05d5\u05d3\u05e9", + "waning_crescent": "Waning crescent", + "waning_gibbous": "Waning gibbous", + "waxing_crescent": "Waxing crescent", + "waxing_gibbous": "Waxing gibbous" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.hu.json b/homeassistant/components/sensor/.translations/moon.hu.json new file mode 100644 index 00000000000000..0fcd02a6961471 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.hu.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Els\u0151 negyed", + "full_moon": "Telihold", + "last_quarter": "Utols\u00f3 negyed", + "new_moon": "\u00dajhold", + "waning_crescent": "Fogy\u00f3 Hold (sarl\u00f3)", + "waning_gibbous": "Fogy\u00f3 Hold", + "waxing_crescent": "N\u00f6v\u0151 Hold (sarl\u00f3)", + "waxing_gibbous": "N\u00f6v\u0151 Hold" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.id.json b/homeassistant/components/sensor/.translations/moon.id.json new file mode 100644 index 00000000000000..3ce14204fb5f8f --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.id.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Babak pertama", + "full_moon": "Bulan purnama", + "last_quarter": "Kuartal terakhir", + "new_moon": "Bulan baru", + "waning_crescent": "Waning crescent", + "waning_gibbous": "Waning gibbous", + "waxing_crescent": "Waxing crescent", + "waxing_gibbous": "Waxing gibbous" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.it.json b/homeassistant/components/sensor/.translations/moon.it.json new file mode 100644 index 00000000000000..39c7f22f7af5f3 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.it.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Primo quarto", + "full_moon": "Luna piena", + "last_quarter": "Ultimo quarto", + "new_moon": "Luna nuova", + "waning_crescent": "Luna calante", + "waning_gibbous": "Gibbosa calante", + "waxing_crescent": "Luna crescente", + "waxing_gibbous": "Gibbosa crescente" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.ko.json b/homeassistant/components/sensor/.translations/moon.ko.json new file mode 100644 index 00000000000000..7e62250b89224a --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.ko.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\ubc18\ub2ec(\ucc28\uc624\ub974\ub294)", + "full_moon": "\ubcf4\ub984\ub2ec", + "last_quarter": "\ubc18\ub2ec(\uc904\uc5b4\ub4dc\ub294)", + "new_moon": "\uc0ad\uc6d4", + "waning_crescent": "\uadf8\ubbd0\ub2ec", + "waning_gibbous": "\ud558\ud604\ub2ec", + "waxing_crescent": "\ucd08\uc2b9\ub2ec", + "waxing_gibbous": "\uc0c1\ud604\ub2ec" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.lb.json b/homeassistant/components/sensor/.translations/moon.lb.json new file mode 100644 index 00000000000000..2aa7ea03db7c28 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.lb.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u00c9ischt V\u00e9ierel", + "full_moon": "Vollmound", + "last_quarter": "L\u00e4scht V\u00e9ierel", + "new_moon": "Neimound", + "waning_crescent": "Ofhuelende Mound", + "waning_gibbous": "Dr\u00ebtt V\u00e9ierel", + "waxing_crescent": "Zouhuelende Mound", + "waxing_gibbous": "Zweet V\u00e9ierel" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.nl.json b/homeassistant/components/sensor/.translations/moon.nl.json new file mode 100644 index 00000000000000..5e78d429b9f079 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.nl.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Eerste kwartier", + "full_moon": "Volle maan", + "last_quarter": "Laatste kwartier", + "new_moon": "Nieuwe maan", + "waning_crescent": "Krimpende, sikkelvormige maan", + "waning_gibbous": "Krimpende, vooruitspringende maan", + "waxing_crescent": "Wassende, sikkelvormige maan", + "waxing_gibbous": "Wassende, vooruitspringende maan" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.nn.json b/homeassistant/components/sensor/.translations/moon.nn.json new file mode 100644 index 00000000000000..7c516bcce50849 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.nn.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Fyrste kvartal", + "full_moon": "Fullm\u00e5ne", + "last_quarter": "Siste kvartal", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Minkande halvm\u00e5ne", + "waning_gibbous": "Minkande m\u00e5ne", + "waxing_crescent": "Veksande halvm\u00e5ne", + "waxing_gibbous": "Veksande m\u00e5ne" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.no.json b/homeassistant/components/sensor/.translations/moon.no.json new file mode 100644 index 00000000000000..19f9985accb412 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.no.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "F\u00f8rste kvarter", + "full_moon": "Fullm\u00e5ne", + "last_quarter": "Siste kvarter", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Minkende halvm\u00e5ne", + "waning_gibbous": "Minkende trekvartm\u00e5ne", + "waxing_crescent": "Voksende halvm\u00e5ne", + "waxing_gibbous": "Voksende trekvartm\u00e5ne" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pl.json b/homeassistant/components/sensor/.translations/moon.pl.json new file mode 100644 index 00000000000000..85dfe79bae4de8 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pl.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "pierwsza kwadra", + "full_moon": "pe\u0142nia", + "last_quarter": "ostatnia kwadra", + "new_moon": "n\u00f3w", + "waning_crescent": "sierp ubywaj\u0105cy", + "waning_gibbous": "ubywaj\u0105cy garbaty", + "waxing_crescent": "sierp przybywaj\u0105cy", + "waxing_gibbous": "przybywaj\u0105cy garbaty" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/sensor/.translations/moon.pt-BR.json new file mode 100644 index 00000000000000..93b17784a4ebe6 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pt-BR.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Quarto crescente", + "full_moon": "Lua cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Lua Nova", + "waning_crescent": "Minguante", + "waning_gibbous": "Minguante gibosa", + "waxing_crescent": "Crescente", + "waxing_gibbous": "Crescente gibosa" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pt.json b/homeassistant/components/sensor/.translations/moon.pt.json new file mode 100644 index 00000000000000..14961ab98f0874 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pt.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Quarto crescente", + "full_moon": "Lua cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Lua nova", + "waning_crescent": "Lua crescente", + "waning_gibbous": "Minguante convexa", + "waxing_crescent": "Lua minguante", + "waxing_gibbous": "Crescente convexa" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.ro.json b/homeassistant/components/sensor/.translations/moon.ro.json new file mode 100644 index 00000000000000..6f64e497c74229 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.ro.json @@ -0,0 +1,6 @@ +{ + "state": { + "full_moon": "Lun\u0103 plin\u0103", + "new_moon": "Lun\u0103 nou\u0103" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.ru.json b/homeassistant/components/sensor/.translations/moon.ru.json new file mode 100644 index 00000000000000..6db932a1aed0ae --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.ru.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044c", + "full_moon": "\u041f\u043e\u043b\u043d\u043e\u043b\u0443\u043d\u0438\u0435", + "last_quarter": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044c", + "new_moon": "\u041d\u043e\u0432\u043e\u043b\u0443\u043d\u0438\u0435", + "waning_crescent": "\u0421\u0442\u0430\u0440\u0430\u044f \u043b\u0443\u043d\u0430", + "waning_gibbous": "\u0423\u0431\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u043b\u0443\u043d\u0430", + "waxing_crescent": "\u041c\u043e\u043b\u043e\u0434\u0430\u044f \u043b\u0443\u043d\u0430", + "waxing_gibbous": "\u041f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u043b\u0443\u043d\u0430" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.sl.json b/homeassistant/components/sensor/.translations/moon.sl.json new file mode 100644 index 00000000000000..1b69e10e6f9832 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.sl.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Prvi krajec", + "full_moon": "Polna luna", + "last_quarter": "Zadnji krajec", + "new_moon": "Mlaj", + "waning_crescent": "Zadnji izbo\u010dec", + "waning_gibbous": "Zadnji srpec", + "waxing_crescent": "Prvi izbo\u010dec", + "waxing_gibbous": "Prvi srpec" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.sv.json b/homeassistant/components/sensor/.translations/moon.sv.json new file mode 100644 index 00000000000000..ae69c1c9654939 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.sv.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "F\u00f6rsta kvartalet", + "full_moon": "Fullm\u00e5ne", + "last_quarter": "Sista kvartalet", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Avtagande halvm\u00e5ne", + "waning_gibbous": "Avtagande halvm\u00e5ne", + "waxing_crescent": "Tilltagande halvm\u00e5ne", + "waxing_gibbous": "Tilltagande halvm\u00e5ne" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.uk.json b/homeassistant/components/sensor/.translations/moon.uk.json new file mode 100644 index 00000000000000..2467a705d50a32 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.uk.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u041f\u0435\u0440\u0448\u0430 \u0447\u0432\u0435\u0440\u0442\u044c", + "full_moon": "\u041f\u043e\u0432\u043d\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", + "last_quarter": "\u041e\u0441\u0442\u0430\u043d\u043d\u044f \u0447\u0432\u0435\u0440\u0442\u044c", + "new_moon": "\u041d\u043e\u0432\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", + "waning_crescent": "\u0417\u0440\u043e\u0441\u0442\u0430\u044e\u0447\u0438\u0439 \u043f\u0456\u0432\u043c\u0456\u0441\u044f\u0446\u044c", + "waning_gibbous": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", + "waxing_crescent": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c", + "waxing_gibbous": "\u041c\u043e\u043b\u043e\u0434\u0438\u0439 \u043c\u0456\u0441\u044f\u0446\u044c" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.zh-Hans.json b/homeassistant/components/sensor/.translations/moon.zh-Hans.json new file mode 100644 index 00000000000000..22ab0d49f62d06 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.zh-Hans.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u4e0a\u5f26\u6708", + "full_moon": "\u6ee1\u6708", + "last_quarter": "\u4e0b\u5f26\u6708", + "new_moon": "\u65b0\u6708", + "waning_crescent": "\u6b8b\u6708", + "waning_gibbous": "\u4e8f\u51f8\u6708", + "waxing_crescent": "\u5ce8\u7709\u6708", + "waxing_gibbous": "\u76c8\u51f8\u6708" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.zh-Hant.json b/homeassistant/components/sensor/.translations/moon.zh-Hant.json new file mode 100644 index 00000000000000..9cf4aad011e04e --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.zh-Hant.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "\u4e0a\u5f26\u6708", + "full_moon": "\u6eff\u6708", + "last_quarter": "\u4e0b\u5f26\u6708", + "new_moon": "\u65b0\u6708", + "waning_crescent": "\u6b98\u6708", + "waning_gibbous": "\u8667\u51f8\u6708", + "waxing_crescent": "\u86fe\u7709\u6708", + "waxing_gibbous": "\u76c8\u51f8\u6708" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.af.json b/homeassistant/components/sensor/.translations/season.af.json new file mode 100644 index 00000000000000..0dbe4a131eec87 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.af.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Herfs", + "spring": "Lente", + "summer": "Somer", + "winter": "Winter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.bg.json b/homeassistant/components/sensor/.translations/season.bg.json new file mode 100644 index 00000000000000..e3865ca42e5993 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.bg.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u0415\u0441\u0435\u043d", + "spring": "\u041f\u0440\u043e\u043b\u0435\u0442", + "summer": "\u041b\u044f\u0442\u043e", + "winter": "\u0417\u0438\u043c\u0430" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.ca.json b/homeassistant/components/sensor/.translations/season.ca.json new file mode 100644 index 00000000000000..9bce187ec65d91 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.ca.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Tardor", + "spring": "Primavera", + "summer": "Estiu", + "winter": "Hivern" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.cs.json b/homeassistant/components/sensor/.translations/season.cs.json new file mode 100644 index 00000000000000..e2d7e7919be30f --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.cs.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Podzim", + "spring": "Jaro", + "summer": "L\u00e9to", + "winter": "Zima" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.cy.json b/homeassistant/components/sensor/.translations/season.cy.json new file mode 100644 index 00000000000000..0d1553ac3ea9bc --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.cy.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Hydref", + "spring": "Gwanwyn", + "summer": "Haf", + "winter": "Gaeaf" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.da.json b/homeassistant/components/sensor/.translations/season.da.json new file mode 100644 index 00000000000000..9cded2f9c0f4cd --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.da.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Efter\u00e5r", + "spring": "For\u00e5r", + "summer": "Sommer", + "winter": "Vinter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.de.json b/homeassistant/components/sensor/.translations/season.de.json new file mode 100644 index 00000000000000..50d702340b9b4b --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.de.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Herbst", + "spring": "Fr\u00fchling", + "summer": "Sommer", + "winter": "Winter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.en.json b/homeassistant/components/sensor/.translations/season.en.json new file mode 100644 index 00000000000000..b42100215ca7ad --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.en.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Autumn", + "spring": "Spring", + "summer": "Summer", + "winter": "Winter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.es-419.json b/homeassistant/components/sensor/.translations/season.es-419.json new file mode 100644 index 00000000000000..65df6a58b10799 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.es-419.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Oto\u00f1o", + "spring": "Primavera", + "summer": "Verano", + "winter": "Invierno" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.es.json b/homeassistant/components/sensor/.translations/season.es.json new file mode 100644 index 00000000000000..65df6a58b10799 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.es.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Oto\u00f1o", + "spring": "Primavera", + "summer": "Verano", + "winter": "Invierno" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.et.json b/homeassistant/components/sensor/.translations/season.et.json new file mode 100644 index 00000000000000..1415a3b907b29f --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.et.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "S\u00fcgis", + "spring": "Kevad", + "summer": "Suvi", + "winter": "Talv" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.eu.json b/homeassistant/components/sensor/.translations/season.eu.json new file mode 100644 index 00000000000000..f226d920043c50 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.eu.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Udazkeneko", + "spring": "Spring", + "summer": "Uda", + "winter": "Winter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.fi.json b/homeassistant/components/sensor/.translations/season.fi.json new file mode 100644 index 00000000000000..f01f6451549ee4 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.fi.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Syksy", + "spring": "Kev\u00e4t", + "summer": "Kes\u00e4", + "winter": "Talvi" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.fr.json b/homeassistant/components/sensor/.translations/season.fr.json new file mode 100644 index 00000000000000..ec9f9657428917 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.fr.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Automne", + "spring": "Printemps", + "summer": "\u00c9t\u00e9", + "winter": "Hiver" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.he.json b/homeassistant/components/sensor/.translations/season.he.json new file mode 100644 index 00000000000000..282c24f3ad964d --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.he.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u05e1\u05ea\u05d9\u05d5", + "spring": "\u05d0\u05d1\u05d9\u05d1", + "summer": "\u05e7\u05d9\u05e5", + "winter": "\u05d7\u05d5\u05e8\u05e3" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.hu.json b/homeassistant/components/sensor/.translations/season.hu.json new file mode 100644 index 00000000000000..63596b097842d1 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.hu.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u0150sz", + "spring": "Tavasz", + "summer": "Ny\u00e1r", + "winter": "T\u00e9l" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.id.json b/homeassistant/components/sensor/.translations/season.id.json new file mode 100644 index 00000000000000..ed0666aee36896 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.id.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Musim gugur", + "spring": "Musim semi", + "summer": "Musim panas", + "winter": "Musim dingin" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.it.json b/homeassistant/components/sensor/.translations/season.it.json new file mode 100644 index 00000000000000..d9138f6b16ebd7 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.it.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Autunno", + "spring": "Primavera", + "summer": "Estate", + "winter": "Inverno" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.ja.json b/homeassistant/components/sensor/.translations/season.ja.json new file mode 100644 index 00000000000000..e441b1aa8ac62e --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.ja.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u79cb", + "spring": "\u6625", + "summer": "\u590f", + "winter": "\u51ac" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.ko.json b/homeassistant/components/sensor/.translations/season.ko.json new file mode 100644 index 00000000000000..f2bf0a7bae5eb7 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.ko.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\uac00\uc744", + "spring": "\ubd04", + "summer": "\uc5ec\ub984", + "winter": "\uaca8\uc6b8" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.lb.json b/homeassistant/components/sensor/.translations/season.lb.json new file mode 100644 index 00000000000000..f33afde7a07ef2 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.lb.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Hierscht", + "spring": "Fr\u00e9ijoer", + "summer": "Summer", + "winter": "Wanter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.lv.json b/homeassistant/components/sensor/.translations/season.lv.json new file mode 100644 index 00000000000000..a96e1112f71f31 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.lv.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Rudens", + "spring": "Pavasaris", + "summer": "Vasara", + "winter": "Ziema" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.nl.json b/homeassistant/components/sensor/.translations/season.nl.json new file mode 100644 index 00000000000000..6054a8e2be5c79 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.nl.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Herfst", + "spring": "Lente", + "summer": "Zomer", + "winter": "Winter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.nn.json b/homeassistant/components/sensor/.translations/season.nn.json new file mode 100644 index 00000000000000..dbcff7ef81970a --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.nn.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Haust", + "spring": "V\u00e5r", + "summer": "Sommar", + "winter": "Vinter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.no.json b/homeassistant/components/sensor/.translations/season.no.json new file mode 100644 index 00000000000000..9d520dae6a5614 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.no.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "H\u00f8st", + "spring": "V\u00e5r", + "summer": "Sommer", + "winter": "Vinter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.pl.json b/homeassistant/components/sensor/.translations/season.pl.json new file mode 100644 index 00000000000000..f5a7da57e7f4b1 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.pl.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Jesie\u0144", + "spring": "Wiosna", + "summer": "Lato", + "winter": "Zima" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.pt-BR.json b/homeassistant/components/sensor/.translations/season.pt-BR.json new file mode 100644 index 00000000000000..fde45ad6c8efa0 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.pt-BR.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Outono", + "spring": "Primavera", + "summer": "Ver\u00e3o", + "winter": "Inverno" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.pt.json b/homeassistant/components/sensor/.translations/season.pt.json new file mode 100644 index 00000000000000..fde45ad6c8efa0 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.pt.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Outono", + "spring": "Primavera", + "summer": "Ver\u00e3o", + "winter": "Inverno" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.ro.json b/homeassistant/components/sensor/.translations/season.ro.json new file mode 100644 index 00000000000000..04f90318290a95 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.ro.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Toamn\u0103", + "spring": "Prim\u0103var\u0103", + "summer": "Var\u0103", + "winter": "Iarn\u0103" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.ru.json b/homeassistant/components/sensor/.translations/season.ru.json new file mode 100644 index 00000000000000..2b04886b72d955 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.ru.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u041e\u0441\u0435\u043d\u044c", + "spring": "\u0412\u0435\u0441\u043d\u0430", + "summer": "\u041b\u0435\u0442\u043e", + "winter": "\u0417\u0438\u043c\u0430" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.sl.json b/homeassistant/components/sensor/.translations/season.sl.json new file mode 100644 index 00000000000000..f715a3ec13a612 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.sl.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Jesen", + "spring": "Pomlad", + "summer": "Poletje", + "winter": "Zima" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.sv.json b/homeassistant/components/sensor/.translations/season.sv.json new file mode 100644 index 00000000000000..02332d7690652b --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.sv.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "H\u00f6st", + "spring": "V\u00e5r", + "summer": "Sommar", + "winter": "Vinter" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.th.json b/homeassistant/components/sensor/.translations/season.th.json new file mode 100644 index 00000000000000..097997303891a5 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.th.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u0e24\u0e14\u0e39\u0e43\u0e1a\u0e44\u0e21\u0e49\u0e23\u0e48\u0e27\u0e07", + "spring": "\u0e24\u0e14\u0e39\u0e43\u0e1a\u0e44\u0e21\u0e49\u0e1c\u0e25\u0e34", + "summer": "\u0e24\u0e14\u0e39\u0e23\u0e49\u0e2d\u0e19", + "winter": "\u0e24\u0e14\u0e39\u0e2b\u0e19\u0e32\u0e27" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.uk.json b/homeassistant/components/sensor/.translations/season.uk.json new file mode 100644 index 00000000000000..766e59a43dab51 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.uk.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u041e\u0441\u0456\u043d\u044c", + "spring": "\u0412\u0435\u0441\u043d\u0430", + "summer": "\u041b\u0456\u0442\u043e", + "winter": "\u0417\u0438\u043c\u0430" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.vi.json b/homeassistant/components/sensor/.translations/season.vi.json new file mode 100644 index 00000000000000..a3bb21dee270ba --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.vi.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "M\u00f9a thu", + "spring": "M\u00f9a xu\u00e2n", + "summer": "M\u00f9a h\u00e8", + "winter": "M\u00f9a \u0111\u00f4ng" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.zh-Hans.json b/homeassistant/components/sensor/.translations/season.zh-Hans.json new file mode 100644 index 00000000000000..78801f4b1df70d --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.zh-Hans.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u79cb\u5b63", + "spring": "\u6625\u5b63", + "summer": "\u590f\u5b63", + "winter": "\u51ac\u5b63" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/season.zh-Hant.json b/homeassistant/components/sensor/.translations/season.zh-Hant.json new file mode 100644 index 00000000000000..78801f4b1df70d --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.zh-Hant.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "\u79cb\u5b63", + "spring": "\u6625\u5b63", + "summer": "\u590f\u5b63", + "winter": "\u51ac\u5b63" + } +} \ No newline at end of file diff --git a/homeassistant/components/smartthings/.translations/bg.json b/homeassistant/components/smartthings/.translations/bg.json new file mode 100644 index 00000000000000..8a13a76a2a9337 --- /dev/null +++ b/homeassistant/components/smartthings/.translations/bg.json @@ -0,0 +1,28 @@ +{ + "config": { + "error": { + "app_not_installed": "\u041c\u043e\u043b\u044f, \u0443\u0432\u0435\u0440\u0435\u0442\u0435 \u0441\u0435, \u0447\u0435 \u0441\u0442\u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043b\u0438 \u0438 \u043e\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u0430\u043b\u0438 HomeAmistant SmartApp \u0438 \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "app_setup_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435 \u043d\u0430 SmartApp. \u041c\u043e\u043b\u044f \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.", + "base_url_not_https": "`base_url` \u0437\u0430 `http` \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d \u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u0432\u0430 \u0441 `https://`", + "token_already_setup": "\u041a\u043e\u0434\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d.", + "token_forbidden": "\u041a\u043e\u0434\u044a\u0442 \u043d\u044f\u043c\u0430 \u0438\u0437\u0438\u0441\u043a\u0443\u0435\u043c\u0438\u0442\u0435 OAuth \u043f\u0440\u0430\u0432\u0430.", + "token_invalid_format": "\u041a\u043e\u0434\u044a\u0442 \u0442\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u0435 \u0432\u044a\u0432 \u0444\u043e\u0440\u043c\u0430\u0442 UID/GUID", + "token_unauthorized": "\u041a\u043e\u0434\u044a\u0442 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u0438\u043b\u0438 \u0432\u0435\u0447\u0435 \u043d\u0435 \u0435 \u043e\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u0430\u043d.", + "webhook_error": "SmartThings \u043d\u0435 \u043c\u043e\u0436\u0430 \u0434\u0430 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0430 \u0430\u0434\u0440\u0435\u0441\u0430, \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d \u0432 `base_url`. \u041c\u043e\u043b\u044f, \u043f\u0440\u0435\u0433\u043b\u0435\u0434\u0430\u0439\u0442\u0435 \u0438\u0437\u0438\u0441\u043a\u0432\u0430\u043d\u0438\u044f\u0442\u0430 \u0437\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430." + }, + "step": { + "user": { + "data": { + "access_token": "\u041a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f" + }, + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 SmartThings [Personal Access Token] ( {token_url} ), \u043a\u043e\u0439\u0442\u043e \u0435 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u043d \u0441\u043f\u043e\u0440\u0435\u0434 [\u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f\u0442\u0430] ( {component_url} ).", + "title": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f (Personal Access Token)" + }, + "wait_install": { + "description": "\u041c\u043e\u043b\u044f, \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant SmartApp \u043f\u043e\u043d\u0435 \u043d\u0430 \u0435\u0434\u043d\u043e \u043c\u044f\u0441\u0442\u043e \u0438 \u043a\u043b\u0438\u043a\u043d\u0435\u0442\u0435 \u0432\u044a\u0440\u0445\u0443 \u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435.", + "title": "\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u0439\u0442\u0435 SmartApp" + } + }, + "title": "SmartThings" + } +} \ No newline at end of file diff --git a/homeassistant/components/smartthings/.translations/fr.json b/homeassistant/components/smartthings/.translations/fr.json index 6503634b92c5b2..56c30acbf1b0e3 100644 --- a/homeassistant/components/smartthings/.translations/fr.json +++ b/homeassistant/components/smartthings/.translations/fr.json @@ -5,17 +5,21 @@ "app_setup_error": "Impossible de configurer la SmartApp. Veuillez r\u00e9essayer.", "base_url_not_https": "Le param\u00e8tre `base_url` du composant` http` doit \u00eatre configur\u00e9 et commencer par `https: //`.", "token_already_setup": "Le jeton a d\u00e9j\u00e0 \u00e9t\u00e9 configur\u00e9.", + "token_forbidden": "Le jeton n'a pas les port\u00e9es OAuth requises.", "token_invalid_format": "Le jeton doit \u00eatre au format UID / GUID", - "token_unauthorized": "Le jeton est invalide ou n'est plus autoris\u00e9." + "token_unauthorized": "Le jeton est invalide ou n'est plus autoris\u00e9.", + "webhook_error": "SmartThings n'a pas pu valider le point de terminaison configur\u00e9 en \u00ab\u00a0base_url\u00a0\u00bb. Veuillez consulter les exigences du composant." }, "step": { "user": { "data": { "access_token": "Jeton d'acc\u00e8s" }, + "description": "Veuillez entrer un [jeton d'acc\u00e8s personnel SmartThings] ( {token_url} ) cr\u00e9\u00e9 selon les [instructions] ( {component_url} ).", "title": "Entrer un jeton d'acc\u00e8s personnel" }, "wait_install": { + "description": "Veuillez installer la SmartApp de Home Assistant dans au moins un emplacement et cliquez sur Soumettre.", "title": "Installer SmartApp" } }, diff --git a/homeassistant/components/sonos/.translations/th.json b/homeassistant/components/sonos/.translations/th.json new file mode 100644 index 00000000000000..4efff6bffb4487 --- /dev/null +++ b/homeassistant/components/sonos/.translations/th.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 Sonos \u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/.translations/bg.json b/homeassistant/components/tellduslive/.translations/bg.json new file mode 100644 index 00000000000000..beb1bc0d6e696b --- /dev/null +++ b/homeassistant/components/tellduslive/.translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "\u0410\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/.translations/fr.json b/homeassistant/components/tellduslive/.translations/fr.json index a20e6bff2b5fc5..a7ddd4c6fa64c0 100644 --- a/homeassistant/components/tellduslive/.translations/fr.json +++ b/homeassistant/components/tellduslive/.translations/fr.json @@ -1,14 +1,26 @@ { "config": { "abort": { - "already_setup": "TelldusLive est d\u00e9j\u00e0 configur\u00e9" + "all_configured": "TelldusLive est d\u00e9j\u00e0 configur\u00e9", + "already_setup": "TelldusLive est d\u00e9j\u00e0 configur\u00e9", + "authorize_url_fail": "Erreur inconnue lors de la g\u00e9n\u00e9ration d'une URL d'autorisation.", + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification d\u00e9pass\u00e9.", + "unknown": "Une erreur inconnue s'est produite" }, "error": { "auth_error": "Erreur d'authentification, veuillez r\u00e9essayer." }, "step": { + "auth": { + "description": "Pour lier votre compte TelldusLive: \n 1. Cliquez sur le lien ci-dessous \n 2. Connectez-vous \u00e0 Telldus Live \n 3. Autorisez ** {app_name} ** (cliquez sur ** Oui **). \n 4. Revenez ici et cliquez sur ** ENVOYER **. \n\n [Lien compte TelldusLive] ( {auth_url} )", + "title": "S\u2019authentifier sur TelldusLive" + }, "user": { - "description": "Vide" + "data": { + "host": "H\u00f4te" + }, + "description": "Vide", + "title": "Choisissez le point de terminaison." } }, "title": "Telldus Live" diff --git a/homeassistant/components/toon/.translations/bg.json b/homeassistant/components/toon/.translations/bg.json new file mode 100644 index 00000000000000..e4aa0d8c08842a --- /dev/null +++ b/homeassistant/components/toon/.translations/bg.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "client_id": "\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u044f\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d.", + "client_secret": "\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u0442\u0430 \u043f\u0430\u0440\u043e\u043b\u0430 \u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430.", + "no_agreements": "\u0422\u043e\u0437\u0438 \u043f\u0440\u043e\u0444\u0438\u043b \u043d\u044f\u043c\u0430 Toon \u0434\u0438\u0441\u043f\u043b\u0435\u0438.", + "no_app": "\u0422\u0440\u044f\u0431\u0432\u0430 \u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442\u0435 Toon, \u043f\u0440\u0435\u0434\u0438 \u0434\u0430 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0441\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0430\u0442\u0435. [\u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0447\u0435\u0442\u0435\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438\u0442\u0435] (https://www.home-assistant.io/components/toon/).", + "unknown_auth_fail": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u043d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f." + }, + "error": { + "credentials": "\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0435\u043d\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u0438 \u0441\u0430 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0438.", + "display_exists": "\u0418\u0437\u0431\u0440\u0430\u043d\u0438\u044f\u0442 \u0434\u0438\u0441\u043f\u043b\u0435\u0439 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d." + }, + "step": { + "authenticate": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + }, + "description": "\u0423\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0441 \u0412\u0430\u0448\u0438\u044f Eneco Toon \u043f\u0440\u043e\u0444\u0438\u043b (\u043d\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0430 \u0437\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u0446\u0438).", + "title": "\u0421\u0432\u044a\u0440\u0436\u0435\u0442\u0435 \u0412\u0430\u0448\u0438\u044f \u0430\u043a\u0430\u0443\u043d\u0442 \u0432 \u0422\u043e\u043e\u043d" + }, + "display": { + "data": { + "display": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0434\u0438\u0441\u043f\u043b\u0435\u0439" + }, + "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0434\u0438\u0441\u043f\u043b\u0435\u044f \u043d\u0430 Toon, \u0441 \u043a\u043e\u0439\u0442\u043e \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435\u0442\u0435.", + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0434\u0438\u0441\u043f\u043b\u0435\u0439" + } + }, + "title": "Toon" + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/.translations/es-419.json b/homeassistant/components/toon/.translations/es-419.json index db064def53b77e..a0ce81495a8bbb 100644 --- a/homeassistant/components/toon/.translations/es-419.json +++ b/homeassistant/components/toon/.translations/es-419.json @@ -13,6 +13,7 @@ "username": "Nombre de usuario" } } - } + }, + "title": "Toon" } } \ No newline at end of file diff --git a/homeassistant/components/toon/.translations/fr.json b/homeassistant/components/toon/.translations/fr.json index 5bf0c60199e0c0..7c41cdc0d246ea 100644 --- a/homeassistant/components/toon/.translations/fr.json +++ b/homeassistant/components/toon/.translations/fr.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "client_id": "L'ID client de la configuration n'est pas valide.", + "client_secret": "Le client secret de la configuration n'est pas valide.", "no_agreements": "Ce compte n'a pas d'affichages Toon.", "no_app": "Vous devez configurer Toon avant de pouvoir vous authentifier avec celui-ci. [Veuillez lire les instructions] (https://www.home-assistant.io/components/toon/).", "unknown_auth_fail": "Une erreur inattendue s'est produite lors de l'authentification." diff --git a/homeassistant/components/tplink/.translations/bg.json b/homeassistant/components/tplink/.translations/bg.json new file mode 100644 index 00000000000000..25ffb753076df3 --- /dev/null +++ b/homeassistant/components/tplink/.translations/bg.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 TP-Link \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430.", + "single_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "step": { + "confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 TP-Link \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430?", + "title": "TP-Link Smart Home" + } + }, + "title": "TP-Link Smart Home" + } +} \ No newline at end of file diff --git a/homeassistant/components/tradfri/.translations/bg.json b/homeassistant/components/tradfri/.translations/bg.json new file mode 100644 index 00000000000000..15d052c758fb9f --- /dev/null +++ b/homeassistant/components/tradfri/.translations/bg.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u0428\u043b\u044e\u0437\u0430 \u0435 \u0432\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0448\u043b\u044e\u0437\u0430.", + "invalid_key": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d\u0435 \u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447. \u0410\u043a\u043e \u0442\u043e\u0432\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0430\u0432\u0430 \u0434\u0430 \u0441\u0435 \u0441\u043b\u0443\u0447\u0432\u0430, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u0434\u0430 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0442\u0435 \u0448\u043b\u044e\u0437\u0430.", + "timeout": "\u0412\u0440\u0435\u043c\u0435\u0442\u043e \u0437\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434\u0430 \u0438\u0437\u0442\u0435\u0447\u0435." + }, + "step": { + "auth": { + "data": { + "host": "\u0410\u0434\u0440\u0435\u0441", + "security_code": "\u041a\u043e\u0434 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442" + }, + "description": "\u041c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u043d\u0430\u043c\u0435\u0440\u0438\u0442\u0435 \u043a\u043e\u0434\u0430 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442 \u043d\u0430 \u0433\u044a\u0440\u0431\u0430 \u043d\u0430 \u0448\u043b\u044e\u0437\u0430.", + "title": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043a\u043e\u0434 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442" + } + }, + "title": "IKEA TR\u00c5DFRI" + } +} \ No newline at end of file diff --git a/homeassistant/components/twilio/.translations/bg.json b/homeassistant/components/twilio/.translations/bg.json new file mode 100644 index 00000000000000..6f06d5c00c628f --- /dev/null +++ b/homeassistant/components/twilio/.translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "one_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/twilio/.translations/pl.json b/homeassistant/components/twilio/.translations/pl.json index 19c835c4b8cb39..2b963ff1be506e 100644 --- a/homeassistant/components/twilio/.translations/pl.json +++ b/homeassistant/components/twilio/.translations/pl.json @@ -5,7 +5,7 @@ "one_instance_allowed": "Wymagana jest tylko jedna instancja." }, "create_entry": { - "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Twilio Webhook]({twilio_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/x-www-form-urlencoded \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." + "default": "Aby wysy\u0142a\u0107 zdarzenia do Home Assistant'a, musisz skonfigurowa\u0107 [Twilio Webhook]({twilio_url}). \n\n Wprowad\u017a nast\u0119puj\u0105ce dane:\n\n - URL: `{webhook_url}` \n - Metoda: POST \n - Typ zawarto\u015bci: application/x-www-form-urlencoded \n\nZapoznaj si\u0119 z [dokumentacj\u0105]({docs_url}) na temat konfiguracji automatyzacji, by obs\u0142u\u017cy\u0107 przychodz\u0105ce dane." }, "step": { "user": { diff --git a/homeassistant/components/unifi/.translations/bg.json b/homeassistant/components/unifi/.translations/bg.json new file mode 100644 index 00000000000000..beb1bc0d6e696b --- /dev/null +++ b/homeassistant/components/unifi/.translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "\u0410\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/upnp/.translations/bg.json b/homeassistant/components/upnp/.translations/bg.json new file mode 100644 index 00000000000000..a19d2d44159e13 --- /dev/null +++ b/homeassistant/components/upnp/.translations/bg.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "UPnP/IGD \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "no_devices_discovered": "\u041d\u044f\u043c\u0430 \u043e\u0442\u043a\u0440\u0438\u0442\u0438 UPnP/IGD", + "no_sensors_or_port_mapping": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0439\u0442\u0435 \u0441\u0435\u043d\u0437\u043e\u0440\u0438\u0442\u0435 \u0438\u043b\u0438 \u043f\u0440\u0435\u043d\u0430\u0441\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u043f\u043e\u0440\u0442\u0430", + "single_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 UPnP/IGD." + }, + "step": { + "init": { + "title": "UPnP/IGD" + }, + "user": { + "data": { + "enable_port_mapping": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u0440\u0435\u043d\u0430\u0441\u043e\u0447\u0432\u0430\u043d\u0435 \u043d\u0430 \u043f\u043e\u0440\u0442\u0430 \u0437\u0430 Home Assistant", + "enable_sensors": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0442\u0440\u0430\u0444\u0438\u0447\u043d\u0438 \u0441\u0435\u043d\u0437\u043e\u0440\u0438", + "igd": "UPnP/IGD" + }, + "title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u043e\u043f\u0446\u0438\u0438 \u0437\u0430 UPnP/IGD" + } + }, + "title": "UPnP/IGD" + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/.translations/fr.json b/homeassistant/components/zha/.translations/fr.json new file mode 100644 index 00000000000000..de1a2274dd3489 --- /dev/null +++ b/homeassistant/components/zha/.translations/fr.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Une seule configuration de ZHA est autoris\u00e9e." + }, + "error": { + "cannot_connect": "Impossible de se connecter au p\u00e9riph\u00e9rique ZHA." + }, + "step": { + "user": { + "data": { + "usb_path": "Chemin du p\u00e9riph\u00e9rique USB" + }, + "title": "ZHA" + } + }, + "title": "ZHA" + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave/.translations/bg.json b/homeassistant/components/zwave/.translations/bg.json new file mode 100644 index 00000000000000..7140e3956df79c --- /dev/null +++ b/homeassistant/components/zwave/.translations/bg.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Z-Wave \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "one_instance_only": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u0441\u0430\u043c\u043e \u0435\u0434\u0438\u043d Z-Wave \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440." + }, + "error": { + "option_error": "\u0412\u0430\u043b\u0438\u0434\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Z-Wave \u043d\u0435 \u0431\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e. \u041f\u0440\u0430\u0432\u0438\u043b\u0435\u043d \u043b\u0438 \u0435 \u043f\u044a\u0442\u044f\u0442 \u043a\u044a\u043c USB \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e?" + }, + "step": { + "user": { + "data": { + "network_key": "\u041c\u0440\u0435\u0436\u043e\u0432 \u043a\u043b\u044e\u0447 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d\u0435)", + "usb_path": "USB \u043f\u044a\u0442" + }, + "description": "\u0412\u0438\u0436\u0442\u0435 https://www.home-assistant.io/docs/z-wave/installation/ \u0437\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u043d\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0438\u0442\u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u043b\u0438\u0432\u0438", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435 \u043d\u0430 Z-Wave" + } + }, + "title": "Z-Wave" + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave/.translations/pl.json b/homeassistant/components/zwave/.translations/pl.json index a96010a74a8b00..c392f0093a0d35 100644 --- a/homeassistant/components/zwave/.translations/pl.json +++ b/homeassistant/components/zwave/.translations/pl.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "network_key": "Klucz sieciowy (pozostaw pusty by generowa\u0107 automatycznie)", + "network_key": "Klucz sieciowy (pozostaw pusty, by generowa\u0107 automatycznie)", "usb_path": "\u015acie\u017cka do kontrolera Z-Wave USB" }, "description": "Zobacz https://www.home-assistant.io/docs/z-wave/installation/, aby uzyska\u0107 informacje na temat zmiennych konfiguracyjnych", From ee1c270c8949404cb8f670739d6d1864a15fc457 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2019 12:56:59 -0700 Subject: [PATCH 074/605] Updated frontend to 20190321.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 6e05299ec52b6c..30b9d350df6d1d 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190320.0'] +REQUIREMENTS = ['home-assistant-frontend==20190321.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 87e9f53bba9ffc..c9878e43ff85f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -554,7 +554,7 @@ hole==0.3.0 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190320.0 +home-assistant-frontend==20190321.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 935bc5689e1832..885ca887d65e3d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -126,7 +126,7 @@ hdate==0.8.7 holidays==0.9.9 # homeassistant.components.frontend -home-assistant-frontend==20190320.0 +home-assistant-frontend==20190321.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From bc85d47878ac6cfdf94145859de8e541f1d2c2e4 Mon Sep 17 00:00:00 2001 From: Ryan Bahm Date: Thu, 21 Mar 2019 12:59:07 -0700 Subject: [PATCH 075/605] Change .now() to .utcnow() (#22233) ephem works in UTC time, not local time. As a result, our comparison needs to be in UTC for accurate results. --- homeassistant/components/season/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/season/sensor.py b/homeassistant/components/season/sensor.py index c9b222f2b26617..84a2b426e9e4b1 100644 --- a/homeassistant/components/season/sensor.py +++ b/homeassistant/components/season/sensor.py @@ -130,5 +130,5 @@ def icon(self): def update(self): """Update season.""" - self.datetime = datetime.now() + self.datetime = datetime.utcnow() self.season = get_season(self.datetime, self.hemisphere, self.type) From a84ba90c9e974c5741a0735407f401bad0346652 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 20 Mar 2019 22:15:21 -0400 Subject: [PATCH 076/605] Fix ZHA force polled entities. (#22222) ## Description: Fix "force_polled" ZHA entities. ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- homeassistant/components/zha/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index d0848222549340..1e98118e09f87c 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -179,7 +179,7 @@ def async_restore_last_state(self, last_state): async def async_update(self): """Retrieve latest state.""" - for channel in self.cluster_channels: + for channel in self.cluster_channels.values(): if hasattr(channel, 'async_update'): await channel.async_update() From 1aab2840125f00ef0e9bfe2d596fc61605483311 Mon Sep 17 00:00:00 2001 From: Karim Roukoz Date: Thu, 21 Mar 2019 12:39:30 -0400 Subject: [PATCH 077/605] Bump total-connect-client to 0.25, fixing issue with Total Connect (#22230) * Bump total-connect-client to 0.25 * Bump version in requirements_all.txt --- homeassistant/components/alarm_control_panel/totalconnect.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/totalconnect.py b/homeassistant/components/alarm_control_panel/totalconnect.py index ba8155fde930f5..a272a22abe509d 100644 --- a/homeassistant/components/alarm_control_panel/totalconnect.py +++ b/homeassistant/components/alarm_control_panel/totalconnect.py @@ -18,7 +18,7 @@ STATE_ALARM_ARMED_CUSTOM_BYPASS) -REQUIREMENTS = ['total_connect_client==0.24'] +REQUIREMENTS = ['total_connect_client==0.25'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index c9878e43ff85f8..88e1c0492c9656 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1710,7 +1710,7 @@ todoist-python==7.0.17 toonapilib==3.2.2 # homeassistant.components.alarm_control_panel.totalconnect -total_connect_client==0.24 +total_connect_client==0.25 # homeassistant.components.tplink_lte tp-connected==0.0.4 From 0f730310a4a0e485836234cfdae08896b5b26cb3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2019 13:03:40 -0700 Subject: [PATCH 078/605] Bumped version to 0.90.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index df0146cde62c72..ba33a566c9ac52 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 56cbe8a73f02a989ad0eab026c52441d804124b2 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Thu, 21 Mar 2019 10:31:55 -0400 Subject: [PATCH 079/605] Stream fixes (#22238) * fix issues with out of order packets, and empty first packet on some IP camera models * do not skip the first packet --- homeassistant/components/stream/worker.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 3a3e19d970330a..d0196761968b38 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -63,21 +63,29 @@ def stream_worker(hass, stream, quit_event): first_packet = True sequence = 1 audio_packets = {} + last_dts = None while not quit_event.is_set(): try: packet = next(container.demux(video_stream)) if packet.dts is None: + if first_packet: + continue # If we get a "flushing" packet, the stream is done - raise StopIteration + raise StopIteration("No dts in packet") except (av.AVError, StopIteration) as ex: # End of stream, clear listeners and stop thread for fmt, _ in outputs.items(): hass.loop.call_soon_threadsafe( stream.outputs[fmt].put, None) - _LOGGER.error("Error demuxing stream: %s", ex) + _LOGGER.error("Error demuxing stream: %s", str(ex)) break + # Skip non monotonically increasing dts in feed + if not first_packet and last_dts >= packet.dts: + continue + last_dts = packet.dts + # Reset segment on every keyframe if packet.is_keyframe: # Save segment to outputs From b2bb70b5aa2ce73f4d586730a964f119b0b1f0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Arnauts?= Date: Thu, 21 Mar 2019 16:24:30 +0100 Subject: [PATCH 080/605] Allow on/off on tado climate component. (#22242) * Allow on/off on tado climate component. Bump python-tado version. Patch from @wmalgadey * Revert wrongly change in tado device tracker --- homeassistant/components/tado/__init__.py | 2 +- homeassistant/components/tado/climate.py | 29 +++++++++++++++++-- .../components/tado/device_tracker.py | 4 +-- requirements_all.txt | 2 +- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 56fc0cb704c573..6808729685eb11 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,7 +10,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.util import Throttle -REQUIREMENTS = ['python-tado==0.2.8'] +REQUIREMENTS = ['python-tado==0.2.9'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index d5f152bbd76d5e..c68a427cb473d6 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -4,9 +4,10 @@ from homeassistant.const import (PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( - SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) + SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_ON_OFF) +from homeassistant.const import ( + ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.util.temperature import convert as convert_temperature -from homeassistant.const import ATTR_TEMPERATURE from homeassistant.components.tado import DATA_TADO _LOGGER = logging.getLogger(__name__) @@ -41,7 +42,8 @@ CONST_MODE_OFF: 'Off', } -SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE +SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE | + SUPPORT_ON_OFF) def setup_platform(hass, config, add_entities, discovery_info=None): @@ -199,6 +201,27 @@ def target_temperature(self): """Return the temperature we try to reach.""" return self._target_temp + @property + def is_on(self): + """Return true if heater is on.""" + return self._device_is_active + + def turn_off(self): + """Turn device off.""" + _LOGGER.info("Switching mytado.com to OFF for zone %s", + self.zone_name) + + self._current_operation = CONST_MODE_OFF + self._control_heating() + + def turn_on(self): + """Turn device on.""" + _LOGGER.info("Switching mytado.com to %s mode for zone %s", + self._overlay_mode, self.zone_name) + + self._current_operation = self._overlay_mode + self._control_heating() + def set_temperature(self, **kwargs): """Set new target temperature.""" temperature = kwargs.get(ATTR_TEMPERATURE) diff --git a/homeassistant/components/tado/device_tracker.py b/homeassistant/components/tado/device_tracker.py index 8804bef561622a..7812bbd812bbd5 100644 --- a/homeassistant/components/tado/device_tracker.py +++ b/homeassistant/components/tado/device_tracker.py @@ -52,9 +52,9 @@ def __init__(self, hass, config): # If there's a home_id, we need a different API URL if self.home_id is None: - self.tadoapiurl = 'https://auth.tado.com/api/v2/me' + self.tadoapiurl = 'https://my.tado.com/api/v2/me' else: - self.tadoapiurl = 'https://auth.tado.com/api/v2' \ + self.tadoapiurl = 'https://my.tado.com/api/v2' \ '/homes/{home_id}/mobileDevices' # The API URL always needs a username and password diff --git a/requirements_all.txt b/requirements_all.txt index 88e1c0492c9656..9cdda45585f33b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1394,7 +1394,7 @@ python-songpal==0.0.9.1 python-synology==0.2.0 # homeassistant.components.tado -python-tado==0.2.8 +python-tado==0.2.9 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 From f47a50aa243f1fbd196fbbd2d5397510505ab6a0 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Thu, 21 Mar 2019 11:13:20 -0500 Subject: [PATCH 081/605] Fix validate webhook requirements (#22248) --- .../components/smartthings/smartapp.py | 2 ++ tests/components/smartthings/test_init.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index 0b64bac595676f..548a38711bd176 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -65,6 +65,8 @@ def validate_webhook_requirements(hass: HomeAssistantType) -> bool: """Ensure HASS is setup properly to receive webhooks.""" if cloud.async_active_subscription(hass): return True + if hass.data[DOMAIN][CONF_CLOUDHOOK_URL] is not None: + return True return get_webhook_url(hass).lower().startswith('https://') diff --git a/tests/components/smartthings/test_init.py b/tests/components/smartthings/test_init.py index a5edc93fce64bc..4daf37cac550a1 100644 --- a/tests/components/smartthings/test_init.py +++ b/tests/components/smartthings/test_init.py @@ -189,6 +189,33 @@ async def test_config_entry_loads_platforms( assert forward_mock.call_count == len(SUPPORTED_PLATFORMS) +async def test_config_entry_loads_unconnected_cloud( + hass, config_entry, app, installed_app, + device, smartthings_mock, subscription_factory, scene): + """Test entry loads during startup when cloud isn't connected.""" + setattr(hass.config_entries, '_entries', [config_entry]) + hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud" + hass.config.api.base_url = 'http://0.0.0.0' + api = smartthings_mock.return_value + api.app.return_value = mock_coro(return_value=app) + api.installed_app.return_value = mock_coro(return_value=installed_app) + api.devices.side_effect = \ + lambda *args, **kwargs: mock_coro(return_value=[device]) + api.scenes.return_value = mock_coro(return_value=[scene]) + mock_token = Mock() + mock_token.access_token.return_value = str(uuid4()) + mock_token.refresh_token.return_value = str(uuid4()) + api.generate_tokens.return_value = mock_coro(return_value=mock_token) + subscriptions = [subscription_factory(capability) + for capability in device.capabilities] + api.subscriptions.return_value = mock_coro(return_value=subscriptions) + with patch.object(hass.config_entries, 'async_forward_entry_setup', + return_value=mock_coro()) as forward_mock: + assert await smartthings.async_setup_entry(hass, config_entry) + await hass.async_block_till_done() + assert forward_mock.call_count == len(SUPPORTED_PLATFORMS) + + async def test_unload_entry(hass, config_entry): """Test entries are unloaded correctly.""" connect_disconnect = Mock() From 8a314d7da0c12e9ef1c1f69714026ed3f9d03724 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 Mar 2019 19:39:24 +0100 Subject: [PATCH 082/605] Update Hass-NabuCasa 0.9 (#22258) --- homeassistant/components/cloud/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index ff1b2344ac89c8..75874d6759ef48 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.8'] +REQUIREMENTS = ['hass-nabucasa==0.9'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 9cdda45585f33b..8703c4a0ca2c2f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -524,7 +524,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.8 +hass-nabucasa==0.9 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 885ca887d65e3d..7dd40c05d8ed58 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.8 +hass-nabucasa==0.9 # homeassistant.components.mqtt.server hbmqtt==0.9.4 From ff93591aaf4dbe028e078c64bf393adfd0106459 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 21 Mar 2019 13:22:08 -0700 Subject: [PATCH 083/605] Lint --- homeassistant/components/tado/climate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index c68a427cb473d6..f698906caa743c 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -1,7 +1,6 @@ """Support for Tado to create a climate device for each zone.""" import logging -from homeassistant.const import (PRECISION_TENTHS, TEMP_CELSIUS) from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_ON_OFF) From 86a510441cb606e42a84627415b331e0d44bb0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Thu, 21 Mar 2019 21:46:25 +0100 Subject: [PATCH 084/605] Upgrade tibber libary, support solar production (#22261) --- homeassistant/components/tibber/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index 9c5f375e19f400..eeae1587099b01 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.9.8'] +REQUIREMENTS = ['pyTibber==0.9.9'] DOMAIN = 'tibber' diff --git a/requirements_all.txt b/requirements_all.txt index 349f8d483045ab..80ee0ea3da68d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -922,7 +922,7 @@ pyRFXtrx==0.23 # pySwitchmate==0.4.5 # homeassistant.components.tibber -pyTibber==0.9.8 +pyTibber==0.9.9 # homeassistant.components.dlink.switch pyW215==0.6.0 From 5716d0aa1a625f707a85d4a79b2eaa26a4d5afb0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 Mar 2019 22:21:13 +0100 Subject: [PATCH 085/605] Update hass-nabucasa 0.10 (#22267) --- homeassistant/components/cloud/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 75874d6759ef48..9517971b16d8d1 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.9'] +REQUIREMENTS = ['hass-nabucasa==0.10'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 80ee0ea3da68d5..ec922ff34da105 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -518,7 +518,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.9 +hass-nabucasa==0.10 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b88e6b97db2d2b..c910e97747aaa6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.9 +hass-nabucasa==0.10 # homeassistant.components.mqtt.server hbmqtt==0.9.4 From 8085e9206a7437b8ccd2c1bd7be454b12eaf6d16 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 Mar 2019 22:21:13 +0100 Subject: [PATCH 086/605] Update hass-nabucasa 0.10 (#22267) --- homeassistant/components/cloud/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 75874d6759ef48..9517971b16d8d1 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.9'] +REQUIREMENTS = ['hass-nabucasa==0.10'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 8703c4a0ca2c2f..45d9386cbd8cba 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -524,7 +524,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.9 +hass-nabucasa==0.10 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7dd40c05d8ed58..3340bb40d9cf31 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.9 +hass-nabucasa==0.10 # homeassistant.components.mqtt.server hbmqtt==0.9.4 From 3432c5da9e79fb635003d9518fb9cffb00f2ab5d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 22 Mar 2019 03:40:11 +0100 Subject: [PATCH 087/605] Upgrade sqlalchemy to 1.3.0 (#22269) --- homeassistant/components/recorder/__init__.py | 2 +- homeassistant/components/sql/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 6c338457b34721..0df1fa42ad4913 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -25,7 +25,7 @@ from .const import DATA_INSTANCE from .util import session_scope -REQUIREMENTS = ['sqlalchemy==1.2.18'] +REQUIREMENTS = ['sqlalchemy==1.3.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index bd246c0d01cfdb..bc40d5efb4279f 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['sqlalchemy==1.2.18'] +REQUIREMENTS = ['sqlalchemy==1.3.0'] CONF_COLUMN_NAME = 'column' CONF_QUERIES = 'queries' diff --git a/requirements_all.txt b/requirements_all.txt index ec922ff34da105..d6d82bd42eabff 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1632,7 +1632,7 @@ spotipy-homeassistant==2.4.4.dev1 # homeassistant.components.recorder # homeassistant.components.sql.sensor -sqlalchemy==1.2.18 +sqlalchemy==1.3.0 # homeassistant.components.srp_energy.sensor srpenergy==1.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c910e97747aaa6..adcd2db3c43100 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -289,7 +289,7 @@ somecomfort==0.5.2 # homeassistant.components.recorder # homeassistant.components.sql.sensor -sqlalchemy==1.2.18 +sqlalchemy==1.3.0 # homeassistant.components.srp_energy.sensor srpenergy==1.0.5 From 30d4a9f12c31924ab0995086b052ff7f19447a03 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Thu, 21 Mar 2019 21:41:41 -0500 Subject: [PATCH 088/605] Plex: Avoid refreshing by both device and session methods (#22266) * Avoid refreshing by both device and session methods * Fix indentation --- homeassistant/components/plex/media_player.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 35000fa35c345b..f3dd9dfc1a7772 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -170,21 +170,31 @@ def update_devices(): config, device, None, plex_sessions, update_devices, update_sessions) plex_clients[device.machineIdentifier] = new_client + _LOGGER.debug("New device: %s", device.machineIdentifier) new_plex_clients.append(new_client) else: + _LOGGER.debug("Refreshing device: %s", + device.machineIdentifier) plex_clients[device.machineIdentifier].refresh(device, None) # add devices with a session and no client (ex. PlexConnect Apple TV's) if config.get(CONF_INCLUDE_NON_CLIENTS): for machine_identifier, (session, player) in plex_sessions.items(): + if machine_identifier in available_client_ids: + # Avoid using session if already added as a device. + _LOGGER.debug("Skipping session, device exists: %s", + machine_identifier) + continue if (machine_identifier not in plex_clients and machine_identifier is not None): new_client = PlexClient( config, player, session, plex_sessions, update_devices, update_sessions) plex_clients[machine_identifier] = new_client + _LOGGER.debug("New session: %s", machine_identifier) new_plex_clients.append(new_client) else: + _LOGGER.debug("Refreshing session: %s", machine_identifier) plex_clients[machine_identifier].refresh(None, session) clients_to_remove = [] From c90e13bfef7425fb54b86c49db6ead96e7363c3b Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Fri, 22 Mar 2019 01:25:31 -0700 Subject: [PATCH 089/605] Bump androidtv to 0.0.13 (#22279) * Bump androidtv to 0.0.13 * Bump androidtv to 0.0.13 in requirements_all.txt --- homeassistant/components/androidtv/media_player.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 1282a40cac5a54..5ec4ef325f08f7 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.12'] +REQUIREMENTS = ['androidtv==0.0.13'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index d6d82bd42eabff..b82d38d409d7f9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -155,7 +155,7 @@ alpha_vantage==2.1.0 amcrest==1.2.7 # homeassistant.components.androidtv.media_player -androidtv==0.0.12 +androidtv==0.0.13 # homeassistant.components.anel_pwrctrl.switch anel_pwrctrl-homeassistant==0.0.1.dev2 From b125514655e8769842ab20f2c32e3fae7d2336a3 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Fri, 22 Mar 2019 05:47:06 -0700 Subject: [PATCH 090/605] Improved exception handling and logging (#22268) Catch RuntimeError exceptions. Don't log ADB command output if there isn't any. --- homeassistant/components/androidtv/media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 5ec4ef325f08f7..1d186484f40543 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -163,7 +163,7 @@ def service_adb_command(service): output = target_device.adb_command(cmd) # log the output if there is any - if output: + if output and (not isinstance(output, str) or output.strip()): _LOGGER.info("Output of command '%s' from '%s': %s", cmd, target_device.entity_id, repr(output)) @@ -223,7 +223,7 @@ def __init__(self, aftv, name, apps): TcpTimeoutException) else: # Using "pure-python-adb" (communicate with ADB server) - self.exceptions = (ConnectionResetError,) + self.exceptions = (ConnectionResetError, RuntimeError) # Property attributes self._available = self.aftv.available From 2b6e197deb61c3d08afa9d598f867467a2a66fc1 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 22 Mar 2019 14:43:39 +0100 Subject: [PATCH 091/605] Consolidate the netgear_lte configuration (#22105) * Consolidate the netgear_lte configuration * Simplfications from review * Extract sensor_types * Simplify defaults --- .../components/netgear_lte/__init__.py | 63 +++++++++++++++---- .../components/netgear_lte/notify.py | 43 ++++++------- .../components/netgear_lte/sensor.py | 29 +++++---- .../components/netgear_lte/sensor_types.py | 8 +++ 4 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 homeassistant/components/netgear_lte/sensor_types.py diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index 5f8c680b7f02b6..34330426e34232 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -8,12 +8,17 @@ import voluptuous as vol from homeassistant.const import ( - CONF_HOST, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP) + CONF_HOST, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_PASSWORD, + CONF_RECIPIENT, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback -from homeassistant.helpers import config_validation as cv +from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.util import Throttle +from . import sensor_types + REQUIREMENTS = ['eternalegypt==0.0.5'] _LOGGER = logging.getLogger(__name__) @@ -23,10 +28,26 @@ DOMAIN = 'netgear_lte' DATA_KEY = 'netgear_lte' + +NOTIFY_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME, default=DOMAIN): cv.string, + vol.Optional(CONF_RECIPIENT, default=[]): + vol.All(cv.ensure_list, [cv.string]), +}) + +SENSOR_SCHEMA = vol.Schema({ + vol.Optional(CONF_MONITORED_CONDITIONS, default=sensor_types.DEFAULT): + vol.All(cv.ensure_list, [vol.In(sensor_types.ALL)]), +}) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.All(cv.ensure_list, [vol.Schema({ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(NOTIFY_DOMAIN, default={}): + vol.All(cv.ensure_list, [NOTIFY_SCHEMA]), + vol.Optional(SENSOR_DOMAIN, default={}): + SENSOR_SCHEMA, })]) }, extra=vol.ALLOW_EXTRA) @@ -71,13 +92,8 @@ class LTEData: modem_data = attr.ib(init=False, factory=dict) def get_modem_data(self, config): - """Get the requested or the only modem_data value.""" - if CONF_HOST in config: - return self.modem_data.get(config[CONF_HOST]) - if len(self.modem_data) == 1: - return next(iter(self.modem_data.values())) - - return None + """Get modem_data for the host in config.""" + return self.modem_data.get(config[CONF_HOST]) async def async_setup(hass, config): @@ -87,9 +103,32 @@ async def async_setup(hass, config): hass, cookie_jar=aiohttp.CookieJar(unsafe=True)) hass.data[DATA_KEY] = LTEData(websession) - tasks = [_setup_lte(hass, conf) for conf in config.get(DOMAIN, [])] - if tasks: - await asyncio.wait(tasks) + netgear_lte_config = config[DOMAIN] + + # Set up each modem + tasks = [_setup_lte(hass, lte_conf) for lte_conf in netgear_lte_config] + await asyncio.wait(tasks) + + # Load platforms for each modem + for lte_conf in netgear_lte_config: + # Notify + for notify_conf in lte_conf[NOTIFY_DOMAIN]: + discovery_info = { + CONF_HOST: lte_conf[CONF_HOST], + CONF_NAME: notify_conf.get(CONF_NAME), + NOTIFY_DOMAIN: notify_conf, + } + hass.async_create_task(discovery.async_load_platform( + hass, NOTIFY_DOMAIN, DOMAIN, discovery_info, config)) + + # Sensor + sensor_conf = lte_conf.get(SENSOR_DOMAIN) + discovery_info = { + CONF_HOST: lte_conf[CONF_HOST], + SENSOR_DOMAIN: sensor_conf, + } + hass.async_create_task(discovery.async_load_platform( + hass, SENSOR_DOMAIN, DOMAIN, discovery_info, config)) return True diff --git a/homeassistant/components/netgear_lte/notify.py b/homeassistant/components/netgear_lte/notify.py index 20a20b2129182a..fba1a335ace333 100644 --- a/homeassistant/components/netgear_lte/notify.py +++ b/homeassistant/components/netgear_lte/notify.py @@ -2,28 +2,23 @@ import logging import attr -import voluptuous as vol from homeassistant.components.notify import ( - ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) -from homeassistant.const import CONF_HOST -import homeassistant.helpers.config_validation as cv + ATTR_TARGET, BaseNotificationService, DOMAIN) -from ..netgear_lte import DATA_KEY +from . import CONF_RECIPIENT, DATA_KEY DEPENDENCIES = ['netgear_lte'] _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_HOST): cv.string, - vol.Required(ATTR_TARGET): vol.All(cv.ensure_list, [cv.string]), -}) - async def async_get_service(hass, config, discovery_info=None): """Get the notification service.""" - return NetgearNotifyService(hass, config) + if discovery_info is None: + return + + return NetgearNotifyService(hass, discovery_info) @attr.s @@ -35,17 +30,23 @@ class NetgearNotifyService(BaseNotificationService): async def async_send_message(self, message="", **kwargs): """Send a message to a user.""" + import eternalegypt + modem_data = self.hass.data[DATA_KEY].get_modem_data(self.config) if not modem_data: - _LOGGER.error("No modem available") + _LOGGER.error("Modem not ready") + return + + targets = kwargs.get(ATTR_TARGET, self.config[DOMAIN][CONF_RECIPIENT]) + if not targets: + _LOGGER.warning("No recipients") + return + + if not message: return - phone = self.config.get(ATTR_TARGET) - targets = kwargs.get(ATTR_TARGET, phone) - if targets and message: - for target in targets: - import eternalegypt - try: - await modem_data.modem.sms(target, message) - except eternalegypt.Error: - _LOGGER.error("Unable to send to %s", target) + for target in targets: + try: + await modem_data.modem.sms(target, message) + except eternalegypt.Error: + _LOGGER.error("Unable to send to %s", target) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 339fa678d61cf4..774cdc5536a1fd 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -1,37 +1,36 @@ """Support for Netgear LTE sensors.""" +import logging + import attr -import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_HOST, CONF_SENSORS +from homeassistant.components.sensor import DOMAIN from homeassistant.exceptions import PlatformNotReady -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -from ..netgear_lte import DATA_KEY +from . import CONF_MONITORED_CONDITIONS, DATA_KEY +from .sensor_types import SENSOR_SMS, SENSOR_USAGE DEPENDENCIES = ['netgear_lte'] -SENSOR_SMS = 'sms' -SENSOR_USAGE = 'usage' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_HOST): cv.string, - vol.Required(CONF_SENSORS): vol.All( - cv.ensure_list, [vol.In([SENSOR_SMS, SENSOR_USAGE])]), -}) +_LOGGER = logging.getLogger(__name__) async def async_setup_platform( hass, config, async_add_entities, discovery_info): """Set up Netgear LTE sensor devices.""" - modem_data = hass.data[DATA_KEY].get_modem_data(config) + if discovery_info is None: + return + + modem_data = hass.data[DATA_KEY].get_modem_data(discovery_info) if not modem_data: raise PlatformNotReady + sensor_conf = discovery_info[DOMAIN] + monitored_conditions = sensor_conf[CONF_MONITORED_CONDITIONS] + sensors = [] - for sensor_type in config[CONF_SENSORS]: + for sensor_type in monitored_conditions: if sensor_type == SENSOR_SMS: sensors.append(SMSSensor(modem_data, sensor_type)) elif sensor_type == SENSOR_USAGE: diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py new file mode 100644 index 00000000000000..b05ecf6074a9f8 --- /dev/null +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -0,0 +1,8 @@ +"""Define possible sensor types.""" + +SENSOR_SMS = 'sms' +SENSOR_USAGE = 'usage' + +ALL = [SENSOR_SMS, SENSOR_USAGE] + +DEFAULT = [SENSOR_USAGE] From 1ddc65a0ce74141499faeee1036bdb75917ef670 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Sat, 23 Mar 2019 02:59:10 +0800 Subject: [PATCH 092/605] Fixing the api_streams sensor (#22200) * Fire events with websocket messages. * Added tests to validate * Fixed api_streams sensor to use new sensor * Delete from coverageac as now works. * Removed websocket request event. * Use dispatcher instead of events. * Moved sensor to under websocket_api * Changes as per code review * Fixed tests. * Modified test * Patch --- .coveragerc | 1 - .../components/api_streams/sensor.py | 90 ------------------- .../components/websocket_api/const.py | 4 + .../components/websocket_api/http.py | 9 +- .../components/websocket_api/sensor.py | 53 +++++++++++ tests/components/api_streams/test_sensor.py | 75 ---------------- tests/components/websocket_api/test_auth.py | 48 +++++++++- tests/components/websocket_api/test_sensor.py | 30 +++++++ 8 files changed, 142 insertions(+), 168 deletions(-) delete mode 100644 homeassistant/components/api_streams/sensor.py create mode 100644 homeassistant/components/websocket_api/sensor.py delete mode 100644 tests/components/api_streams/test_sensor.py create mode 100644 tests/components/websocket_api/test_sensor.py diff --git a/.coveragerc b/.coveragerc index e3ad5ff206e295..67b0c9f76a939e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -415,7 +415,6 @@ omit = homeassistant/components/lifx_cloud/scene.py homeassistant/components/scsgate/* homeassistant/components/sense/* - homeassistant/components/api_streams/sensor.py homeassistant/components/aftership/sensor.py homeassistant/components/airvisual/sensor.py homeassistant/components/alpha_vantage/sensor.py diff --git a/homeassistant/components/api_streams/sensor.py b/homeassistant/components/api_streams/sensor.py deleted file mode 100644 index 1fbef2c58966da..00000000000000 --- a/homeassistant/components/api_streams/sensor.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Entity to track connections to stream API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.api_streams/ -""" -import logging - -from homeassistant.const import EVENT_HOMEASSISTANT_STOP -from homeassistant.core import callback -from homeassistant.helpers.entity import Entity - -NAME_WS = 'homeassistant.components.websocket_api' -NAME_STREAM = 'homeassistant.components.api' - - -class StreamHandler(logging.Handler): - """Check log messages for stream connect/disconnect.""" - - def __init__(self, entity): - """Initialize handler.""" - super().__init__() - self.entity = entity - self.count = 0 - - def handle(self, record): - """Handle a log message.""" - if record.name == NAME_STREAM: - if not record.msg.startswith('STREAM'): - return - - if record.msg.endswith('ATTACHED'): - self.entity.count += 1 - elif record.msg.endswith('RESPONSE CLOSED'): - self.entity.count -= 1 - - else: - if not record.msg.startswith('WS'): - return - if len(record.args) < 2: - return - if record.args[1] == 'Connected': - self.entity.count += 1 - elif record.args[1] == 'Closed connection': - self.entity.count -= 1 - - self.entity.schedule_update_ha_state() - - -async def async_setup_platform( - hass, config, async_add_entities, discovery_info=None): - """Set up the API streams platform.""" - entity = APICount() - handler = StreamHandler(entity) - - logging.getLogger(NAME_STREAM).addHandler(handler) - logging.getLogger(NAME_WS).addHandler(handler) - - @callback - def remove_logger(event): - """Remove our handlers.""" - logging.getLogger(NAME_STREAM).removeHandler(handler) - logging.getLogger(NAME_WS).removeHandler(handler) - - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, remove_logger) - - async_add_entities([entity]) - - -class APICount(Entity): - """Entity to represent how many people are connected to the stream API.""" - - def __init__(self): - """Initialize the API count.""" - self.count = 0 - - @property - def name(self): - """Return name of entity.""" - return "Connected clients" - - @property - def state(self): - """Return current API count.""" - return self.count - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return "clients" diff --git a/homeassistant/components/websocket_api/const.py b/homeassistant/components/websocket_api/const.py index 01145275b3161f..4c3e0d564dc8fb 100644 --- a/homeassistant/components/websocket_api/const.py +++ b/homeassistant/components/websocket_api/const.py @@ -20,3 +20,7 @@ # Originally, this was just asyncio.CancelledError, but issue #9546 showed # that futures.CancelledErrors can also occur in some situations. CANCELLATION_ERRORS = (asyncio.CancelledError, futures.CancelledError) + +# Event types +SIGNAL_WEBSOCKET_CONNECTED = 'websocket_connected' +SIGNAL_WEBSOCKET_DISCONNECTED = 'websocket_disconnected' diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index 1ab2b09d7fa6f7..85cb256df9009d 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -13,7 +13,9 @@ from homeassistant.components.http import HomeAssistantView from homeassistant.helpers.json import JSONEncoder -from .const import MAX_PENDING_MSG, CANCELLATION_ERRORS, URL, ERR_UNKNOWN_ERROR +from .const import ( + MAX_PENDING_MSG, CANCELLATION_ERRORS, URL, ERR_UNKNOWN_ERROR, + SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED) from .auth import AuthPhase, auth_required_message from .error import Disconnect from .messages import error_message @@ -142,6 +144,8 @@ def handle_hass_stop(event): self._logger.debug("Received %s", msg) connection = await auth.async_handle(msg) + self.hass.helpers.dispatcher.async_dispatcher_send( + SIGNAL_WEBSOCKET_CONNECTED) # Command phase while not wsock.closed: @@ -192,4 +196,7 @@ def handle_hass_stop(event): else: self._logger.warning("Disconnected: %s", disconnect_warn) + self.hass.helpers.dispatcher.async_dispatcher_send( + SIGNAL_WEBSOCKET_DISCONNECTED) + return wsock diff --git a/homeassistant/components/websocket_api/sensor.py b/homeassistant/components/websocket_api/sensor.py new file mode 100644 index 00000000000000..fd9108c0513c17 --- /dev/null +++ b/homeassistant/components/websocket_api/sensor.py @@ -0,0 +1,53 @@ +"""Entity to track connections to websocket API.""" + +from homeassistant.core import callback +from homeassistant.helpers.entity import Entity + +from .const import SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the API streams platform.""" + entity = APICount() + + # pylint: disable=protected-access + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_CONNECTED, entity._increment) + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_DISCONNECTED, entity._decrement) + + async_add_entities([entity]) + + +class APICount(Entity): + """Entity to represent how many people are connected to the stream API.""" + + def __init__(self): + """Initialize the API count.""" + self.count = 0 + + @property + def name(self): + """Return name of entity.""" + return "Connected clients" + + @property + def state(self): + """Return current API count.""" + return self.count + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return "clients" + + @callback + def _increment(self): + self.count += 1 + self.async_schedule_update_ha_state() + + @callback + def _decrement(self): + self.count -= 1 + self.async_schedule_update_ha_state() diff --git a/tests/components/api_streams/test_sensor.py b/tests/components/api_streams/test_sensor.py deleted file mode 100644 index 48ab7e1c3f9f7e..00000000000000 --- a/tests/components/api_streams/test_sensor.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Test cases for the API stream sensor.""" -import asyncio -import logging -import pytest - -from homeassistant.bootstrap import async_setup_component -from tests.common import assert_setup_component - - -@pytest.mark.skip(reason="test fails randomly due to race condition.") -async def test_api_streams(hass): - """Test API streams.""" - log = logging.getLogger('homeassistant.components.api') - - with assert_setup_component(1): - await async_setup_component(hass, 'sensor', { - 'sensor': { - 'platform': 'api_streams', - } - }) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '0' - - log.debug('STREAM 1 ATTACHED') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '1' - - log.debug('STREAM 1 ATTACHED') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '2' - - log.debug('STREAM 1 RESPONSE CLOSED') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '1' - - -@pytest.mark.skip(reason="test fails randomly due to race condition.") -async def test_websocket_api(hass): - """Test API streams.""" - log = logging.getLogger('homeassistant.components.websocket_api') - - with assert_setup_component(1): - await async_setup_component(hass, 'sensor', { - 'sensor': { - 'platform': 'api_streams', - } - }) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '0' - - log.debug('WS %s: %s', id(log), 'Connected') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '1' - - log.debug('WS %s: %s', id(log), 'Connected') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '2' - - log.debug('WS %s: %s', id(log), 'Closed connection') - await asyncio.sleep(0.1) - - state = hass.states.get('sensor.connected_clients') - assert state.state == '1' diff --git a/tests/components/websocket_api/test_auth.py b/tests/components/websocket_api/test_auth.py index e2c6e303326fc5..cd940708497665 100644 --- a/tests/components/websocket_api/test_auth.py +++ b/tests/components/websocket_api/test_auth.py @@ -1,7 +1,8 @@ """Test auth of websocket API.""" from unittest.mock import patch -from homeassistant.components.websocket_api.const import URL +from homeassistant.components.websocket_api.const import ( + URL, SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED) from homeassistant.components.websocket_api.auth import ( TYPE_AUTH, TYPE_AUTH_INVALID, TYPE_AUTH_OK, TYPE_AUTH_REQUIRED) @@ -24,6 +25,28 @@ async def test_auth_via_msg(no_auth_websocket_client, legacy_auth): assert msg['type'] == TYPE_AUTH_OK +async def test_auth_events(hass, no_auth_websocket_client, legacy_auth): + """Test authenticating.""" + connected_evt = [] + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_CONNECTED, + lambda: connected_evt.append(1)) + disconnected_evt = [] + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_DISCONNECTED, + lambda: disconnected_evt.append(1)) + + await test_auth_via_msg(no_auth_websocket_client, legacy_auth) + + assert len(connected_evt) == 1 + assert not disconnected_evt + + await no_auth_websocket_client.close() + await hass.async_block_till_done() + + assert len(disconnected_evt) == 1 + + async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client): """Test authenticating.""" with patch('homeassistant.components.websocket_api.auth.' @@ -41,6 +64,29 @@ async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client): assert msg['message'] == 'Invalid access token or password' +async def test_auth_events_incorrect_pass(hass, no_auth_websocket_client): + """Test authenticating.""" + connected_evt = [] + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_CONNECTED, + lambda: connected_evt.append(1)) + disconnected_evt = [] + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_DISCONNECTED, + lambda: disconnected_evt.append(1)) + + await test_auth_via_msg_incorrect_pass(no_auth_websocket_client) + + assert not connected_evt + assert not disconnected_evt + + await no_auth_websocket_client.close() + await hass.async_block_till_done() + + assert not connected_evt + assert not disconnected_evt + + async def test_pre_auth_only_auth_allowed(no_auth_websocket_client): """Verify that before authentication, only auth messages are allowed.""" await no_auth_websocket_client.send_json({ diff --git a/tests/components/websocket_api/test_sensor.py b/tests/components/websocket_api/test_sensor.py new file mode 100644 index 00000000000000..b02cc53f38d971 --- /dev/null +++ b/tests/components/websocket_api/test_sensor.py @@ -0,0 +1,30 @@ +"""Test cases for the API stream sensor.""" + +from homeassistant.bootstrap import async_setup_component + +from tests.common import assert_setup_component +from .test_auth import test_auth_via_msg + + +async def test_websocket_api(hass, no_auth_websocket_client, legacy_auth): + """Test API streams.""" + with assert_setup_component(1): + await async_setup_component(hass, 'sensor', { + 'sensor': { + 'platform': 'websocket_api', + } + }) + + state = hass.states.get('sensor.connected_clients') + assert state.state == '0' + + await test_auth_via_msg(no_auth_websocket_client, legacy_auth) + + state = hass.states.get('sensor.connected_clients') + assert state.state == '1' + + await no_auth_websocket_client.close() + await hass.async_block_till_done() + + state = hass.states.get('sensor.connected_clients') + assert state.state == '0' From ce550206a4ea733a7f0489ad761503bda7bdf7cb Mon Sep 17 00:00:00 2001 From: jjlawren Date: Fri, 22 Mar 2019 15:00:13 -0500 Subject: [PATCH 093/605] Fix progress for Plex media_players (#22224) * Return current position and last updated timestamp needed by UI * Add throttling to position updating * Simplify logic, don't clear position when refreshing * Use seconds * Update homeassistant/components/plex/media_player.py Co-Authored-By: jjlawren * Add throttling to position updating * Simplify logic, don't clear position when refreshing --- homeassistant/components/plex/media_player.py | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index f3dd9dfc1a7772..a68a2faade818e 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -322,6 +322,7 @@ def __init__(self, config, device, session, plex_sessions, self._media_image_url = None self._media_title = None self._media_position = None + self._media_position_updated_at = None # Music self._media_album_artist = None self._media_album_name = None @@ -361,7 +362,6 @@ def _clear_media_details(self): self._media_duration = None self._media_image_url = None self._media_title = None - self._media_position = None # Music self._media_album_artist = None self._media_album_name = None @@ -417,7 +417,21 @@ def refresh(self, device, session): self._make = self._player.device else: self._is_player_available = False - self._media_position = self._session.viewOffset + + # Calculate throttled position for proper progress display. + position = int(self._session.viewOffset / 1000) + now = dt_util.utcnow() + if self._media_position is not None: + pos_diff = (position - self._media_position) + time_diff = now - self._media_position_updated_at + if (pos_diff != 0 and + abs(time_diff.total_seconds() - pos_diff) > 5): + self._media_position_updated_at = now + self._media_position = position + else: + self._media_position_updated_at = now + self._media_position = position + self._media_content_id = self._session.ratingKey self._media_content_rating = getattr( self._session, 'contentRating', None) @@ -426,7 +440,7 @@ def refresh(self, device, session): if self._is_player_active and self._session is not None: self._session_type = self._session.type - self._media_duration = self._session.duration + self._media_duration = int(self._session.duration / 1000) # title (movie name, tv episode name, music song name) self._media_title = self._session.title # media type @@ -621,6 +635,16 @@ def media_duration(self): """Return the duration of current playing media in seconds.""" return self._media_duration + @property + def media_position(self): + """Return the duration of current playing media in seconds.""" + return self._media_position + + @property + def media_position_updated_at(self): + """When was the position of the current playing media valid.""" + return self._media_position_updated_at + @property def media_image_url(self): """Return the image URL of current playing media.""" From a58f1eeddaf44f14f8f1d30485872d7b0aedeeaf Mon Sep 17 00:00:00 2001 From: Leonardo Merza Date: Fri, 22 Mar 2019 16:05:08 -0400 Subject: [PATCH 094/605] Add sort by config and tests for Reddit integration (#22081) * add sort type to reddit sensor add sort type to reddit sensor automated commit 14/03/2019 15:53:15 automated commit 15/03/2019 09:43:39 reddit sensor add tests and sort by config reddit sensor add tests and sort by config reddit sensor add tests and sort by config reddit sensor add tests and sort by config * Add reddit sensor sort_by config and tests * Add reddit sensor sort_by config and tests * Add reddit sensor sort_by config and tests * Add reddit sensor sort_by config and tests * Add reddit sensor sort_by config and tests --- homeassistant/components/reddit/sensor.py | 43 ++++-- tests/components/reddit/__init__.py | 1 + tests/components/reddit/test_sensor.py | 175 ++++++++++++++++++++++ 3 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 tests/components/reddit/__init__.py create mode 100644 tests/components/reddit/test_sensor.py diff --git a/homeassistant/components/reddit/sensor.py b/homeassistant/components/reddit/sensor.py index 1b6a960669cd64..3ba43196551028 100644 --- a/homeassistant/components/reddit/sensor.py +++ b/homeassistant/components/reddit/sensor.py @@ -15,6 +15,7 @@ CONF_CLIENT_ID = 'client_id' CONF_CLIENT_SECRET = 'client_secret' +CONF_SORT_BY = 'sort_by' CONF_SUBREDDITS = 'subreddits' ATTR_ID = 'id' @@ -29,6 +30,10 @@ DEFAULT_NAME = 'Reddit' +DOMAIN = 'reddit' + +LIST_TYPES = ['top', 'controversial', 'hot', 'new'] + SCAN_INTERVAL = timedelta(seconds=300) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -37,6 +42,8 @@ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_SUBREDDITS): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_SORT_BY, default='hot'): + vol.All(cv.string, vol.In(LIST_TYPES)), vol.Optional(CONF_MAXIMUM, default=10): cv.positive_int }) @@ -48,6 +55,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): subreddits = config[CONF_SUBREDDITS] user_agent = '{}_home_assistant_sensor'.format(config[CONF_USERNAME]) limit = config[CONF_MAXIMUM] + sort_by = config[CONF_SORT_BY] try: reddit = praw.Reddit( @@ -63,18 +71,20 @@ def setup_platform(hass, config, add_entities, discovery_info=None): _LOGGER.error("Reddit error %s", err) return - sensors = [RedditSensor(reddit, sub, limit) for sub in subreddits] + sensors = [RedditSensor(reddit, subreddit, limit, sort_by) + for subreddit in subreddits] add_entities(sensors, True) class RedditSensor(Entity): """Representation of a Reddit sensor.""" - def __init__(self, reddit, subreddit: str, limit: int): + def __init__(self, reddit, subreddit: str, limit: int, sort_by: str): """Initialize the Reddit sensor.""" self._reddit = reddit - self._limit = limit self._subreddit = subreddit + self._limit = limit + self._sort_by = sort_by self._subreddit_data = [] @@ -93,7 +103,8 @@ def device_state_attributes(self): """Return the state attributes.""" return { ATTR_SUBREDDIT: self._subreddit, - ATTR_POSTS: self._subreddit_data + ATTR_POSTS: self._subreddit_data, + CONF_SORT_BY: self._sort_by } @property @@ -109,17 +120,19 @@ def update(self): try: subreddit = self._reddit.subreddit(self._subreddit) - - for submission in subreddit.top(limit=self._limit): - self._subreddit_data.append({ - ATTR_ID: submission.id, - ATTR_URL: submission.url, - ATTR_TITLE: submission.title, - ATTR_SCORE: submission.score, - ATTR_COMMENTS_NUMBER: submission.num_comments, - ATTR_CREATED: submission.created, - ATTR_BODY: submission.selftext - }) + if hasattr(subreddit, self._sort_by): + method_to_call = getattr(subreddit, self._sort_by) + + for submission in method_to_call(limit=self._limit): + self._subreddit_data.append({ + ATTR_ID: submission.id, + ATTR_URL: submission.url, + ATTR_TITLE: submission.title, + ATTR_SCORE: submission.score, + ATTR_COMMENTS_NUMBER: submission.num_comments, + ATTR_CREATED: submission.created, + ATTR_BODY: submission.selftext + }) except praw.exceptions.PRAWException as err: _LOGGER.error("Reddit error %s", err) diff --git a/tests/components/reddit/__init__.py b/tests/components/reddit/__init__.py new file mode 100644 index 00000000000000..67e0db82f42339 --- /dev/null +++ b/tests/components/reddit/__init__.py @@ -0,0 +1 @@ +"""Tests for the the Reddit component.""" diff --git a/tests/components/reddit/test_sensor.py b/tests/components/reddit/test_sensor.py new file mode 100644 index 00000000000000..2bb22a0024bf57 --- /dev/null +++ b/tests/components/reddit/test_sensor.py @@ -0,0 +1,175 @@ +"""The tests for the Reddit platform.""" +import copy +import unittest +from unittest.mock import patch + +from homeassistant.components.reddit.sensor import ( + DOMAIN, ATTR_SUBREDDIT, ATTR_POSTS, CONF_SORT_BY, + ATTR_ID, ATTR_URL, ATTR_TITLE, ATTR_SCORE, ATTR_COMMENTS_NUMBER, + ATTR_CREATED, ATTR_BODY) +from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, CONF_MAXIMUM) +from homeassistant.setup import setup_component + +from tests.common import (get_test_home_assistant, + MockDependency) + + +VALID_CONFIG = { + 'sensor': { + 'platform': DOMAIN, + 'client_id': 'test_client_id', + 'client_secret': 'test_client_secret', + CONF_USERNAME: 'test_username', + CONF_PASSWORD: 'test_password', + 'subreddits': ['worldnews', 'news'], + + } +} + +VALID_LIMITED_CONFIG = { + 'sensor': { + 'platform': DOMAIN, + 'client_id': 'test_client_id', + 'client_secret': 'test_client_secret', + CONF_USERNAME: 'test_username', + CONF_PASSWORD: 'test_password', + 'subreddits': ['worldnews', 'news'], + CONF_MAXIMUM: 1 + } +} + + +INVALID_SORT_BY_CONFIG = { + 'sensor': { + 'platform': DOMAIN, + 'client_id': 'test_client_id', + 'client_secret': 'test_client_secret', + CONF_USERNAME: 'test_username', + CONF_PASSWORD: 'test_password', + 'subreddits': ['worldnews', 'news'], + 'sort_by': 'invalid_sort_by' + } +} + + +class ObjectView(): + """Use dict properties as attributes.""" + + def __init__(self, d): + """Set dict as internal dict.""" + self.__dict__ = d + + +MOCK_RESULTS = { + 'results': [ + ObjectView({ + 'id': 0, + 'url': 'http://example.com/1', + 'title': 'example1', + 'score': '1', + 'num_comments': '1', + 'created': '', + 'selftext': 'example1 selftext' + }), + ObjectView({ + 'id': 1, + 'url': 'http://example.com/2', + 'title': 'example2', + 'score': '2', + 'num_comments': '2', + 'created': '', + 'selftext': 'example2 selftext' + }) + ] +} + +MOCK_RESULTS_LENGTH = len(MOCK_RESULTS['results']) + + +class MockPraw(): + """Mock class for tmdbsimple library.""" + + def __init__(self, client_id: str, client_secret: + str, username: str, password: str, + user_agent: str): + """Add mock data for API return.""" + self._data = MOCK_RESULTS + + def subreddit(self, subreddit: str): + """Return an instance of a sunbreddit.""" + return MockSubreddit(subreddit, self._data) + + +class MockSubreddit(): + """Mock class for a subreddit instance.""" + + def __init__(self, subreddit: str, data): + """Add mock data for API return.""" + self._subreddit = subreddit + self._data = data + + def top(self, limit): + """Return top posts for a subreddit.""" + return self._return_data(limit) + + def controversial(self, limit): + """Return controversial posts for a subreddit.""" + return self._return_data(limit) + + def hot(self, limit): + """Return hot posts for a subreddit.""" + return self._return_data(limit) + + def new(self, limit): + """Return new posts for a subreddit.""" + return self._return_data(limit) + + def _return_data(self, limit): + """Test method to return modified data.""" + data = copy.deepcopy(self._data) + return data['results'][:limit] + + +class TestRedditSetup(unittest.TestCase): + """Test the Reddit platform.""" + + def setUp(self): + """Initialize values for this testcase class.""" + self.hass = get_test_home_assistant() + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + + @MockDependency('praw') + @patch('praw.Reddit', new=MockPraw) + def test_setup_with_valid_config(self, mock_praw): + """Test the platform setup with movie configuration.""" + setup_component(self.hass, 'sensor', VALID_CONFIG) + + state = self.hass.states.get('sensor.reddit_worldnews') + assert int(state.state) == MOCK_RESULTS_LENGTH + + state = self.hass.states.get('sensor.reddit_news') + assert int(state.state) == MOCK_RESULTS_LENGTH + + assert state.attributes[ATTR_SUBREDDIT] == 'news' + + assert state.attributes[ATTR_POSTS][0] == { + ATTR_ID: 0, + ATTR_URL: 'http://example.com/1', + ATTR_TITLE: 'example1', + ATTR_SCORE: '1', + ATTR_COMMENTS_NUMBER: '1', + ATTR_CREATED: '', + ATTR_BODY: 'example1 selftext' + } + + assert state.attributes[CONF_SORT_BY] == 'hot' + + @MockDependency('praw') + @patch('praw.Reddit', new=MockPraw) + def test_setup_with_invalid_config(self, mock_praw): + """Test the platform setup with invalid movie configuration.""" + setup_component(self.hass, 'sensor', INVALID_SORT_BY_CONFIG) + assert not self.hass.states.get('sensor.reddit_worldnews') From e9cd9f88be00f5c75b8abacb39b67d3e5bbac79c Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 22 Mar 2019 13:16:17 -0700 Subject: [PATCH 095/605] Fix Prometheus casting issues (#22282) ## Description: Fix Prometheus casting issues **Related issue (if applicable):** fixes #8659. ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- .../components/prometheus/__init__.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/prometheus/__init__.py b/homeassistant/components/prometheus/__init__.py index 9053a872134056..de0de8ae16241f 100644 --- a/homeassistant/components/prometheus/__init__.py +++ b/homeassistant/components/prometheus/__init__.py @@ -103,6 +103,16 @@ def _metric(self, metric, factory, documentation, labels=None): full_metric_name, documentation, labels) return self._metrics[metric] + @staticmethod + def state_as_number(state): + """Return a state casted to a float.""" + try: + value = state_helper.state_as_number(state) + except ValueError: + _LOGGER.warning("Could not convert %s to float", state) + value = 0 + return value + @staticmethod def _labels(state): return { @@ -130,7 +140,7 @@ def _handle_binary_sensor(self, state): self.prometheus_client.Gauge, 'State of the binary sensor (0/1)', ) - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) def _handle_input_boolean(self, state): @@ -139,7 +149,7 @@ def _handle_input_boolean(self, state): self.prometheus_client.Gauge, 'State of the input boolean (0/1)', ) - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) def _handle_device_tracker(self, state): @@ -148,7 +158,7 @@ def _handle_device_tracker(self, state): self.prometheus_client.Gauge, 'State of the device tracker (0/1)', ) - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) def _handle_person(self, state): @@ -157,7 +167,7 @@ def _handle_person(self, state): self.prometheus_client.Gauge, 'State of the person (0/1)', ) - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) def _handle_light(self, state): @@ -171,7 +181,7 @@ def _handle_light(self, state): if 'brightness' in state.attributes: value = state.attributes['brightness'] / 255.0 else: - value = state_helper.state_as_number(state) + value = self.state_as_number(state) value = value * 100 metric.labels(**self._labels(state)).set(value) except ValueError: @@ -183,7 +193,7 @@ def _handle_lock(self, state): self.prometheus_client.Gauge, 'State of the lock (0/1)', ) - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) def _handle_climate(self, state): @@ -209,7 +219,7 @@ def _handle_climate(self, state): 'climate_state', self.prometheus_client.Gauge, 'State of the thermostat (0/1)') try: - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) except ValueError: pass @@ -232,7 +242,7 @@ def _handle_sensor(self, state): state.entity_id) try: - value = state_helper.state_as_number(state) + value = self.state_as_number(state) if unit == TEMP_FAHRENHEIT: value = fahrenheit_to_celsius(value) _metric.labels(**self._labels(state)).set(value) @@ -249,7 +259,7 @@ def _handle_switch(self, state): ) try: - value = state_helper.state_as_number(state) + value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) except ValueError: pass From ecabf92504ec693d658994743b5d8fbad67b365a Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Sat, 23 Mar 2019 04:42:56 +0800 Subject: [PATCH 096/605] Update trait to support auto without ranges. (#21847) --- .../components/google_assistant/trait.py | 52 ++++++++++------ .../google_assistant/test_google_assistant.py | 2 + .../components/google_assistant/test_trait.py | 59 +++++++++++++++++-- 3 files changed, 91 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index f5b959285db4fe..26d1ccc2088a0a 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -583,15 +583,24 @@ def query_attributes(self): if current_humidity is not None: response['thermostatHumidityAmbient'] = current_humidity - if (operation == climate.STATE_AUTO and - climate.ATTR_TARGET_TEMP_HIGH in attrs and - climate.ATTR_TARGET_TEMP_LOW in attrs): - response['thermostatTemperatureSetpointHigh'] = \ - round(temp_util.convert(attrs[climate.ATTR_TARGET_TEMP_HIGH], - unit, TEMP_CELSIUS), 1) - response['thermostatTemperatureSetpointLow'] = \ - round(temp_util.convert(attrs[climate.ATTR_TARGET_TEMP_LOW], - unit, TEMP_CELSIUS), 1) + if operation == climate.STATE_AUTO: + if (supported & climate.SUPPORT_TARGET_TEMPERATURE_HIGH and + supported & climate.SUPPORT_TARGET_TEMPERATURE_LOW): + response['thermostatTemperatureSetpointHigh'] = \ + round(temp_util.convert( + attrs[climate.ATTR_TARGET_TEMP_HIGH], + unit, TEMP_CELSIUS), 1) + response['thermostatTemperatureSetpointLow'] = \ + round(temp_util.convert( + attrs[climate.ATTR_TARGET_TEMP_LOW], + unit, TEMP_CELSIUS), 1) + else: + target_temp = attrs.get(ATTR_TEMPERATURE) + if target_temp is not None: + target_temp = round( + temp_util.convert(target_temp, unit, TEMP_CELSIUS), 1) + response['thermostatTemperatureSetpointHigh'] = target_temp + response['thermostatTemperatureSetpointLow'] = target_temp else: target_temp = attrs.get(ATTR_TEMPERATURE) if target_temp is not None: @@ -651,12 +660,21 @@ async def execute(self, command, data, params): "Lower bound for temperature range should be between " "{} and {}".format(min_temp, max_temp)) + supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) + svc_data = { + ATTR_ENTITY_ID: self.state.entity_id, + } + + if(supported & climate.SUPPORT_TARGET_TEMPERATURE_HIGH and + supported & climate.SUPPORT_TARGET_TEMPERATURE_LOW): + svc_data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high + svc_data[climate.ATTR_TARGET_TEMP_LOW] = temp_low + else: + svc_data[ATTR_TEMPERATURE] = (temp_high + temp_low) / 2 + await self.hass.services.async_call( - climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, { - ATTR_ENTITY_ID: self.state.entity_id, - climate.ATTR_TARGET_TEMP_HIGH: temp_high, - climate.ATTR_TARGET_TEMP_LOW: temp_low, - }, blocking=True, context=data.context) + climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, svc_data, + blocking=True, context=data.context) elif command == COMMAND_THERMOSTAT_SET_MODE: target_mode = params['thermostatMode'] @@ -669,10 +687,8 @@ async def execute(self, command, data, params): (SERVICE_TURN_ON if target_mode == STATE_ON else SERVICE_TURN_OFF), - { - ATTR_ENTITY_ID: self.state.entity_id, - climate.ATTR_OPERATION_MODE: target_mode, - }, blocking=True, context=data.context) + {ATTR_ENTITY_ID: self.state.entity_id}, + blocking=True, context=data.context) elif supported & climate.SUPPORT_OPERATION_MODE: await self.hass.services.async_call( climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE, { diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index fbaf1b47898528..69964a11fdcbf1 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -107,6 +107,8 @@ def hass_fixture(loop, hass): return hass +# pylint: disable=redefined-outer-name + @asyncio.coroutine def test_sync_request(hass_fixture, assistant_client, auth_header): diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 3af60e2f014c0e..9d067f3314f865 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -649,7 +649,9 @@ async def test_temperature_setting_climate_onoff(hass): trt = trait.TemperatureSettingTrait(hass, State( 'climate.bla', climate.STATE_AUTO, { ATTR_SUPPORTED_FEATURES: ( - climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF | + climate.SUPPORT_TARGET_TEMPERATURE_HIGH | + climate.SUPPORT_TARGET_TEMPERATURE_LOW), climate.ATTR_OPERATION_MODE: climate.STATE_COOL, climate.ATTR_OPERATION_LIST: [ climate.STATE_COOL, @@ -692,7 +694,10 @@ async def test_temperature_setting_climate_range(hass): 'climate.bla', climate.STATE_AUTO, { climate.ATTR_CURRENT_TEMPERATURE: 70, climate.ATTR_CURRENT_HUMIDITY: 25, - ATTR_SUPPORTED_FEATURES: climate.SUPPORT_OPERATION_MODE, + ATTR_SUPPORTED_FEATURES: + climate.SUPPORT_OPERATION_MODE | + climate.SUPPORT_TARGET_TEMPERATURE_HIGH | + climate.SUPPORT_TARGET_TEMPERATURE_LOW, climate.ATTR_OPERATION_MODE: climate.STATE_AUTO, climate.ATTR_OPERATION_LIST: [ STATE_OFF, @@ -716,7 +721,6 @@ async def test_temperature_setting_climate_range(hass): 'thermostatTemperatureSetpointLow': 18.3, 'thermostatTemperatureSetpointHigh': 23.9, } - assert trt.can_execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {}) assert trt.can_execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, {}) assert trt.can_execute(trait.COMMAND_THERMOSTAT_SET_MODE, {}) @@ -785,7 +789,6 @@ async def test_temperature_setting_climate_setpoint(hass): 'thermostatTemperatureSetpoint': 18, } assert trt.can_execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {}) - assert trt.can_execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, {}) assert trt.can_execute(trait.COMMAND_THERMOSTAT_SET_MODE, {}) calls = async_mock_service( @@ -806,6 +809,54 @@ async def test_temperature_setting_climate_setpoint(hass): } +async def test_temperature_setting_climate_setpoint_auto(hass): + """ + Test TemperatureSetting trait support for climate domain. + + Setpoint in auto mode. + """ + hass.config.units.temperature_unit = TEMP_CELSIUS + + trt = trait.TemperatureSettingTrait(hass, State( + 'climate.bla', climate.STATE_AUTO, { + ATTR_SUPPORTED_FEATURES: ( + climate.SUPPORT_OPERATION_MODE | climate.SUPPORT_ON_OFF), + climate.ATTR_OPERATION_MODE: climate.STATE_AUTO, + climate.ATTR_OPERATION_LIST: [ + STATE_OFF, + climate.STATE_AUTO, + ], + climate.ATTR_MIN_TEMP: 10, + climate.ATTR_MAX_TEMP: 30, + ATTR_TEMPERATURE: 18, + climate.ATTR_CURRENT_TEMPERATURE: 20 + }), BASIC_CONFIG) + assert trt.sync_attributes() == { + 'availableThermostatModes': 'off,on,heatcool', + 'thermostatTemperatureUnit': 'C', + } + assert trt.query_attributes() == { + 'thermostatMode': 'heatcool', + 'thermostatTemperatureAmbient': 20, + 'thermostatTemperatureSetpointHigh': 18, + 'thermostatTemperatureSetpointLow': 18, + } + assert trt.can_execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {}) + assert trt.can_execute(trait.COMMAND_THERMOSTAT_SET_MODE, {}) + + calls = async_mock_service( + hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE) + + await trt.execute( + trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, BASIC_DATA, + {'thermostatTemperatureSetpoint': 19}) + assert len(calls) == 1 + assert calls[0].data == { + ATTR_ENTITY_ID: 'climate.bla', + ATTR_TEMPERATURE: 19 + } + + async def test_lock_unlock_lock(hass): """Test LockUnlock trait locking support for lock domain.""" assert trait.LockUnlockTrait.supported(lock.DOMAIN, lock.SUPPORT_OPEN) From 611597a87bd164543606af06f63583227bcb8e60 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Fri, 22 Mar 2019 22:05:26 +0100 Subject: [PATCH 097/605] Sort code owners alphabetically (#22304) ## Description: Sort the code oweners list alphabetically. **Related issue (if applicable):** fixes # **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io# ## Example entry for `configuration.yaml` (if applicable): ```yaml ``` ## Checklist: - [ ] The code change is tested and works locally. - [ ] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [ ] New files were added to `.coveragerc`. If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- CODEOWNERS | 204 +++++++++++++++++++++++++++-------------------------- 1 file changed, 104 insertions(+), 100 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 33853f1c08b914..e880177380f0e5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -47,44 +47,6 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave homeassistant/components/hassio/* @home-assistant/hassio # Individual platforms -homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell -homeassistant/components/hikvision/binary_sensor.py @mezz64 -homeassistant/components/threshold/binary_sensor.py @fabaff -homeassistant/components/uptimerobot/binary_sensor.py @ludeeus -homeassistant/components/push/camera.py @dgomes -homeassistant/components/yi/camera.py @bachya -homeassistant/components/coolmaster/climate.py @OnFreund -homeassistant/components/ephember/climate.py @ttroy50 -homeassistant/components/eq3btsmart/climate.py @rytilahti -homeassistant/components/mill/climate.py @danielhiversen -homeassistant/components/sensibo/climate.py @andrey-git -homeassistant/components/brunt/cover.py @eavanvalkenburg -homeassistant/components/cover/group.py @cdce8p -homeassistant/components/template/cover.py @PhracturedBlue -homeassistant/components/asuswrt/device_tracker.py @kennedyshead -homeassistant/components/automatic/device_tracker.py @armills -homeassistant/components/bt_smarthub/device_tracker.py @jxwolstenholme -homeassistant/components/huawei_router/device_tracker.py @abmantis -homeassistant/components/quantum_gateway/device_tracker.py @cisasteelersfan -homeassistant/components/synology_srm/device_tracker.py @aerialls -homeassistant/components/tile/device_tracker.py @bachya -homeassistant/components/traccar/device_tracker.py @ludeeus -homeassistant/components/xfinity/device_tracker.py @cisasteelersfan -homeassistant/components/lametric/notify.py @robbiet480 -homeassistant/components/lifx_legacy/light.py @amelchio -homeassistant/components/yeelight/light.py @rytilahti -homeassistant/components/yeelightsunflower/light.py @lindsaymarkward -homeassistant/components/nello/lock.py @pschmitt -homeassistant/components/nuki/lock.py @pschmitt -homeassistant/components/braviatv/media_player.py @robbiet480 -homeassistant/components/emby/media_player.py @mezz64 -homeassistant/components/kodi/media_player.py @armills -homeassistant/components/liveboxplaytv/media_player.py @pschmitt -homeassistant/components/mediaroom/media_player.py @dgomes -homeassistant/components/monoprice/media_player.py @etsinko -homeassistant/components/mpd/media_player.py @fabaff -homeassistant/components/xiaomi_tv/media_player.py @fattdev -homeassistant/components/yamaha_musiccast/media_player.py @jalmeroth homeassistant/components/notify/aws_lambda.py @robbiet480 homeassistant/components/notify/aws_sns.py @robbiet480 homeassistant/components/notify/aws_sqs.py @robbiet480 @@ -99,85 +61,42 @@ homeassistant/components/notify/twilio_call.py @robbiet480 homeassistant/components/notify/twilio_sms.py @robbiet480 homeassistant/components/notify/xmpp.py @fabaff homeassistant/components/notify/yessssms.py @flowolf -homeassistant/components/lifx_cloud/scene.py @amelchio -homeassistant/components/airvisual/sensor.py @bachya -homeassistant/components/alpha_vantage/sensor.py @fabaff -homeassistant/components/bitcoin/sensor.py @fabaff -homeassistant/components/cpuspeed/sensor.py @fabaff -homeassistant/components/cups/sensor.py @fabaff -homeassistant/components/darksky/sensor.py @fabaff -homeassistant/components/discogs/sensor.py @thibmaek -homeassistant/components/file/sensor.py @fabaff -homeassistant/components/filter/sensor.py @dgomes -homeassistant/components/fitbit/sensor.py @robbiet480 -homeassistant/components/fixer/sensor.py @fabaff -homeassistant/components/flunearyou/sensor.py @bachya -homeassistant/components/gearbest/sensor.py @HerrHofrat -homeassistant/components/gitter/sensor.py @fabaff -homeassistant/components/glances/sensor.py @fabaff -homeassistant/components/google_travel_time/sensor.py @robbiet480 -homeassistant/components/gpsd/sensor.py @fabaff -homeassistant/components/gtfs/sensor.py @robbiet480 -homeassistant/components/integration/sensor.py @dgomes -homeassistant/components/irish_rail_transport/sensor.py @ttroy50 -homeassistant/components/jewish_calendar/sensor.py @tsvi -homeassistant/components/launch_library/sensor.py @ludeeus -homeassistant/components/linux_battery/sensor.py @fabaff -homeassistant/components/miflora/sensor.py @danielhiversen @ChristianKuehnel -homeassistant/components/min_max/sensor.py @fabaff -homeassistant/components/moon/sensor.py @fabaff -homeassistant/components/netdata/sensor.py @fabaff -homeassistant/components/nmbs/sensor.py @thibmaek -homeassistant/components/nsw_fuel_station/sensor.py @nickw444 -homeassistant/components/ohmconnect/sensor.py @robbiet480 -homeassistant/components/pi_hole/sensor.py @fabaff -homeassistant/components/pollen/sensor.py @bachya -homeassistant/components/pvoutput/sensor.py @fabaff -homeassistant/components/qnap/sensor.py @colinodell -homeassistant/components/ruter/sensor.py @ludeeus -homeassistant/components/scrape/sensor.py @fabaff -homeassistant/components/serial/sensor.py @fabaff -homeassistant/components/seventeentrack/sensor.py @bachya -homeassistant/components/shodan/sensor.py @fabaff -homeassistant/components/sma/sensor.py @kellerza -homeassistant/components/sql/sensor.py @dgomes -homeassistant/components/statistics/sensor.py @fabaff -homeassistant/components/swiss_*/* @fabaff -homeassistant/components/sytadin/sensor.py @gautric -homeassistant/components/tautulli/sensor.py @ludeeus -homeassistant/components/time_date/sensor.py @fabaff -homeassistant/components/uber/sensor.py @robbiet480 -homeassistant/components/version/sensor.py @fabaff -homeassistant/components/waqi/sensor.py @andrey-git -homeassistant/components/worldclock/sensor.py @fabaff -homeassistant/components/switchbot/switch.py @danielhiversen -homeassistant/components/switchmate/switch.py @danielhiversen homeassistant/components/tts/amazon_polly.py @robbiet480 -homeassistant/components/roomba/vacuum.py @pschmitt -homeassistant/components/weather/__init__.py @fabaff -homeassistant/components/darksky/weather.py @fabaff -homeassistant/components/demo/weather.py @fabaff -homeassistant/components/met/weather.py @danielhiversen -homeassistant/components/openweathermap/weather.py @fabaff # A +homeassistant/components/airvisual/sensor.py @bachya +homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell +homeassistant/components/alpha_vantage/sensor.py @fabaff homeassistant/components/ambient_station/* @bachya homeassistant/components/arduino/* @fabaff -homeassistant/components/axis/* @kane610 homeassistant/components/arest/* @fabaff +homeassistant/components/asuswrt/device_tracker.py @kennedyshead +homeassistant/components/automatic/device_tracker.py @armills +homeassistant/components/axis/* @kane610 # B +homeassistant/components/bitcoin/sensor.py @fabaff homeassistant/components/blink/* @fronzbot homeassistant/components/bmw_connected_drive/* @ChristianKuehnel +homeassistant/components/braviatv/media_player.py @robbiet480 homeassistant/components/broadlink/* @danielhiversen +homeassistant/components/brunt/cover.py @eavanvalkenburg +homeassistant/components/bt_smarthub/device_tracker.py @jxwolstenholme # C homeassistant/components/cloudflare/* @ludeeus +homeassistant/components/coolmaster/climate.py @OnFreund homeassistant/components/counter/* @fabaff +homeassistant/components/cover/group.py @cdce8p +homeassistant/components/cpuspeed/sensor.py @fabaff +homeassistant/components/cups/sensor.py @fabaff # D homeassistant/components/daikin/* @fredrike @rofrantz +homeassistant/components/darksky/* @fabaff +homeassistant/components/discogs/sensor.py @thibmaek homeassistant/components/deconz/* @kane610 +homeassistant/components/demo/weather.py @fabaff homeassistant/components/digital_ocean/* @fabaff homeassistant/components/doorbird/* @oblogic7 homeassistant/components/dweet/* @fabaff @@ -187,95 +106,180 @@ homeassistant/components/ecovacs/* @OverloadUT homeassistant/components/edp_redy/* @abmantis homeassistant/components/eight_sleep/* @mezz64 homeassistant/components/egardia/* @jeroenterheerdt +homeassistant/components/emby/media_player.py @mezz64 +homeassistant/components/ephember/climate.py @ttroy50 +homeassistant/components/eq3btsmart/climate.py @rytilahti homeassistant/components/esphome/* @OttoWinter # F -homeassistant/components/freebox/* @snoof85 +homeassistant/components/file/sensor.py @fabaff +homeassistant/components/filter/sensor.py @dgomes +homeassistant/components/fitbit/sensor.py @robbiet480 +homeassistant/components/fixer/sensor.py @fabaff +homeassistant/components/flunearyou/sensor.py @bachya homeassistant/components/foursquare/* @robbiet480 +homeassistant/components/freebox/* @snoof85 # G +homeassistant/components/gearbest/sensor.py @HerrHofrat +homeassistant/components/gitter/sensor.py @fabaff +homeassistant/components/glances/sensor.py @fabaff +homeassistant/components/google_travel_time/sensor.py @robbiet480 homeassistant/components/googlehome/* @ludeeus +homeassistant/components/gpsd/sensor.py @fabaff +homeassistant/components/gtfs/sensor.py @robbiet480 # H homeassistant/components/harmony/* @ehendrix23 +homeassistant/components/hikvision/binary_sensor.py @mezz64 homeassistant/components/history_graph/* @andrey-git homeassistant/components/hive/* @Rendili @KJonline homeassistant/components/homekit/* @cdce8p homeassistant/components/huawei_lte/* @scop +homeassistant/components/huawei_router/device_tracker.py @abmantis # I homeassistant/components/influxdb/* @fabaff +homeassistant/components/integration/sensor.py @dgomes homeassistant/components/ios/* @robbiet480 homeassistant/components/ipma/* @dgomes +homeassistant/components/irish_rail_transport/sensor.py @ttroy50 + +# J +homeassistant/components/jewish_calendar/sensor.py @tsvi # K homeassistant/components/knx/* @Julius2342 +homeassistant/components/kodi/media_player.py @armills homeassistant/components/konnected/* @heythisisnate # L +homeassistant/components/lametric/notify.py @robbiet480 +homeassistant/components/launch_library/sensor.py @ludeeus homeassistant/components/lifx/* @amelchio +homeassistant/components/lifx_cloud/scene.py @amelchio +homeassistant/components/lifx_legacy/light.py @amelchio +homeassistant/components/linux_battery/sensor.py @fabaff +homeassistant/components/liveboxplaytv/media_player.py @pschmitt homeassistant/components/luftdaten/* @fabaff # M homeassistant/components/matrix/* @tinloaf +homeassistant/components/mediaroom/media_player.py @dgomes homeassistant/components/melissa/* @kennedyshead +homeassistant/components/met/weather.py @danielhiversen +homeassistant/components/miflora/sensor.py @danielhiversen @ChristianKuehnel +homeassistant/components/mill/climate.py @danielhiversen +homeassistant/components/min_max/sensor.py @fabaff homeassistant/components/mobile_app/* @robbiet480 +homeassistant/components/monoprice/media_player.py @etsinko +homeassistant/components/moon/sensor.py @fabaff +homeassistant/components/mpd/media_player.py @fabaff homeassistant/components/mystrom/* @fabaff # N +homeassistant/components/nello/lock.py @pschmitt homeassistant/components/ness_alarm/* @nickw444 +homeassistant/components/netdata/sensor.py @fabaff homeassistant/components/nissan_leaf/* @filcole +homeassistant/components/nmbs/sensor.py @thibmaek homeassistant/components/no_ip/* @fabaff +homeassistant/components/nuki/lock.py @pschmitt +homeassistant/components/nsw_fuel_station/sensor.py @nickw444 # O +homeassistant/components/ohmconnect/sensor.py @robbiet480 homeassistant/components/openuv/* @bachya +homeassistant/components/openweathermap/weather.py @fabaff homeassistant/components/owlet/* @oblogic7 # P +homeassistant/components/pi_hole/sensor.py @fabaff homeassistant/components/plant/* @ChristianKuehnel homeassistant/components/point/* @fredrike +homeassistant/components/pollen/sensor.py @bachya +homeassistant/components/push/camera.py @dgomes +homeassistant/components/pvoutput/sensor.py @fabaff # Q +homeassistant/components/qnap/sensor.py @colinodell +homeassistant/components/quantum_gateway/device_tracker.py @cisasteelersfan homeassistant/components/qwikswitch/* @kellerza # R homeassistant/components/rainmachine/* @bachya -homeassistant/components/rfxtrx/* @danielhiversen homeassistant/components/random/* @fabaff +homeassistant/components/rfxtrx/* @danielhiversen +homeassistant/components/rmvtransport/* @cgtobi +homeassistant/components/roomba/vacuum.py @pschmitt +homeassistant/components/ruter/sensor.py @ludeeus # S +homeassistant/components/scrape/sensor.py @fabaff +homeassistant/components/sensibo/climate.py @andrey-git +homeassistant/components/serial/sensor.py @fabaff +homeassistant/components/seventeentrack/sensor.py @bachya homeassistant/components/shiftr/* @fabaff +homeassistant/components/shodan/sensor.py @fabaff homeassistant/components/simplisafe/* @bachya +homeassistant/components/sma/sensor.py @kellerza homeassistant/components/smartthings/* @andrewsayre homeassistant/components/sonos/* @amelchio homeassistant/components/spaceapi/* @fabaff homeassistant/components/spider/* @peternijssen +homeassistant/components/sql/sensor.py @dgomes +homeassistant/components/statistics/sensor.py @fabaff +homeassistant/components/swiss_*/* @fabaff +homeassistant/components/switchbot/switch.py @danielhiversen +homeassistant/components/switchmate/switch.py @danielhiversen +homeassistant/components/synology_srm/device_tracker.py @aerialls +homeassistant/components/sytadin/sensor.py @gautric # T homeassistant/components/tahoma/* @philklei +homeassistant/components/tautulli/sensor.py @ludeeus homeassistant/components/tellduslive/* @fredrike +homeassistant/components/template/cover.py @PhracturedBlue homeassistant/components/tesla/* @zabuldon homeassistant/components/thethingsnetwork/* @fabaff +homeassistant/components/threshold/binary_sensor.py @fabaff homeassistant/components/tibber/* @danielhiversen +homeassistant/components/tile/device_tracker.py @bachya +homeassistant/components/time_date/sensor.py @fabaff homeassistant/components/tplink/* @rytilahti +homeassistant/components/traccar/device_tracker.py @ludeeus homeassistant/components/tradfri/* @ggravlingen homeassistant/components/toon/* @frenck # U +homeassistant/components/uber/sensor.py @robbiet480 homeassistant/components/unifi/* @kane610 homeassistant/components/upcloud/* @scop homeassistant/components/upnp/* @robbiet480 +homeassistant/components/uptimerobot/binary_sensor.py @ludeeus homeassistant/components/utility_meter/* @dgomes # V homeassistant/components/velux/* @Julius2342 +homeassistant/components/version/sensor.py @fabaff # W +homeassistant/components/waqi/sensor.py @andrey-git +homeassistant/components/weather/__init__.py @fabaff homeassistant/components/wemo/* @sqldiablo +homeassistant/components/worldclock/sensor.py @fabaff # X +homeassistant/components/xfinity/device_tracker.py @cisasteelersfan homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi homeassistant/components/xiaomi_miio/* @rytilahti @syssi +homeassistant/components/xiaomi_tv/media_player.py @fattdev + +# Y +homeassistant/components/yamaha_musiccast/* @jalmeroth +homeassistant/components/yeelight/light.py @rytilahti +homeassistant/components/yeelightsunflower/light.py @lindsaymarkward +homeassistant/components/yi/camera.py @bachya # Z homeassistant/components/zeroconf/* @robbiet480 From 58c23bc2d9713edb4f86ba3e58986f6f2ecc6a4b Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 22 Mar 2019 16:01:21 -0700 Subject: [PATCH 098/605] Update srpenergy library (#22307) Fixes #18899 --- homeassistant/components/srp_energy/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/srp_energy/sensor.py b/homeassistant/components/srp_energy/sensor.py index a8466bd87210e5..4d2cd863b12f7f 100644 --- a/homeassistant/components/srp_energy/sensor.py +++ b/homeassistant/components/srp_energy/sensor.py @@ -19,7 +19,7 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['srpenergy==1.0.5'] +REQUIREMENTS = ['srpenergy==1.0.6'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index b82d38d409d7f9..14dfb457d29c3b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1635,7 +1635,7 @@ spotipy-homeassistant==2.4.4.dev1 sqlalchemy==1.3.0 # homeassistant.components.srp_energy.sensor -srpenergy==1.0.5 +srpenergy==1.0.6 # homeassistant.components.starlingbank.sensor starlingbank==3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index adcd2db3c43100..b532b7b386d530 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -292,7 +292,7 @@ somecomfort==0.5.2 sqlalchemy==1.3.0 # homeassistant.components.srp_energy.sensor -srpenergy==1.0.5 +srpenergy==1.0.6 # homeassistant.components.statsd statsd==3.2.1 From 773c56756399ef774996d791d0fab88f0cea59b2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 22 Mar 2019 16:01:43 -0700 Subject: [PATCH 099/605] Switch from using Google Maps API for elevation to Open Elevation API (#22306) ## Description: Switches elevation helper to use [Open Elevation](https://open-elevation.com/) instead of Google Maps API which now requires a API key. It's a drop in replacement for Google Maps too! **Related issue (if applicable):** fixes #19860 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/util/location.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/util/location.py b/homeassistant/util/location.py index f77d1752d6ad31..d369bd890981d1 100644 --- a/homeassistant/util/location.py +++ b/homeassistant/util/location.py @@ -9,7 +9,7 @@ import requests -ELEVATION_URL = 'http://maps.googleapis.com/maps/api/elevation/json' +ELEVATION_URL = 'https://api.open-elevation.com/api/v1/lookup' IP_API = 'http://ip-api.com/json' IPAPI = 'https://ipapi.co/json/' @@ -70,7 +70,6 @@ def elevation(latitude: float, longitude: float) -> int: ELEVATION_URL, params={ 'locations': '{},{}'.format(latitude, longitude), - 'sensor': 'false', }, timeout=10) except requests.RequestException: From 89221bfab968722eee48550589a7fbb92c3daa57 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Fri, 22 Mar 2019 16:01:58 -0700 Subject: [PATCH 100/605] Fix for embedded MQTT server configuration (#22305) ## Description: Passing in a configuration for the embedded MQTT server has been broken for a while. This fixes that. See related issue number for further details. **Related issue (if applicable):** fixes #18228 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/mqtt/server.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index dda2214ce46d10..3373149a013fab 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -40,12 +40,12 @@ def async_start(hass, password, server_config): from hbmqtt.broker import Broker, BrokerException passwd = tempfile.NamedTemporaryFile() + + gen_server_config, client_config = generate_config(hass, passwd, password) + try: if server_config is None: - server_config, client_config = generate_config( - hass, passwd, password) - else: - client_config = None + server_config = gen_server_config broker = Broker(server_config, hass.loop) yield from broker.start() From 90dfe72d310227733b0d93cfc653d071f40cc7af Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 23 Mar 2019 08:00:13 +0100 Subject: [PATCH 101/605] Upgrade pylast to 3.1.0 (#22302) --- homeassistant/components/lastfm/sensor.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/lastfm/sensor.py b/homeassistant/components/lastfm/sensor.py index bb5a09771c2208..e4e28eff4f1819 100644 --- a/homeassistant/components/lastfm/sensor.py +++ b/homeassistant/components/lastfm/sensor.py @@ -9,7 +9,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pylast==3.0.0'] +REQUIREMENTS = ['pylast==3.1.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 14dfb457d29c3b..99f25919566132 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1116,7 +1116,7 @@ pykwb==0.0.8 pylacrosse==0.3.1 # homeassistant.components.lastfm.sensor -pylast==3.0.0 +pylast==3.1.0 # homeassistant.components.launch_library.sensor pylaunches==0.2.0 From 3c811bbf1a808a8261a193e673f681ff5181c295 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 23 Mar 2019 08:00:43 +0100 Subject: [PATCH 102/605] Upgrade py-cpuinfo to 5.0.0 (#22287) --- homeassistant/components/cpuspeed/sensor.py | 9 ++------- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/cpuspeed/sensor.py b/homeassistant/components/cpuspeed/sensor.py index f69d0b285ba1fa..98d22c20d153b6 100644 --- a/homeassistant/components/cpuspeed/sensor.py +++ b/homeassistant/components/cpuspeed/sensor.py @@ -1,9 +1,4 @@ -""" -Support for displaying the current CPU speed. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.cpuspeed/ -""" +"""Support for displaying the current CPU speed.""" import logging import voluptuous as vol @@ -13,7 +8,7 @@ from homeassistant.const import CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['py-cpuinfo==4.0.0'] +REQUIREMENTS = ['py-cpuinfo==5.0.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 99f25919566132..ac20e50cd8ffba 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -894,7 +894,7 @@ py-august==0.7.0 py-canary==0.5.0 # homeassistant.components.cpuspeed.sensor -py-cpuinfo==4.0.0 +py-cpuinfo==5.0.0 # homeassistant.components.melissa py-melissa-climate==2.0.0 From 5f34d3ccb9a08ca07d69e653dbc6f03d34fb4219 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 23 Mar 2019 12:07:32 +0100 Subject: [PATCH 103/605] Update abbreviation (#22317) --- homeassistant/components/hp_ilo/__init__.py | 2 +- homeassistant/components/hp_ilo/sensor.py | 39 +++++++++------------ 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/hp_ilo/__init__.py b/homeassistant/components/hp_ilo/__init__.py index 3cd6b10b260190..67135b947e4556 100644 --- a/homeassistant/components/hp_ilo/__init__.py +++ b/homeassistant/components/hp_ilo/__init__.py @@ -1 +1 @@ -"""The hp_ilo component.""" +"""The HP Integrated Lights-Out (iLO) component.""" diff --git a/homeassistant/components/hp_ilo/sensor.py b/homeassistant/components/hp_ilo/sensor.py index ac2d5ccb109840..a017f0ee3e8c08 100644 --- a/homeassistant/components/hp_ilo/sensor.py +++ b/homeassistant/components/hp_ilo/sensor.py @@ -1,28 +1,23 @@ -""" -Support for information from HP ILO sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sensor.hp_ilo/ -""" -import logging +"""Support for information from HP iLO sensors.""" from datetime import timedelta +import logging import voluptuous as vol -from homeassistant.const import ( - CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD, CONF_NAME, - CONF_MONITORED_VARIABLES, CONF_VALUE_TEMPLATE, CONF_SENSOR_TYPE, - CONF_UNIT_OF_MEASUREMENT) from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_HOST, CONF_MONITORED_VARIABLES, CONF_NAME, CONF_PASSWORD, CONF_PORT, + CONF_SENSOR_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, + CONF_VALUE_TEMPLATE) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['python-hpilo==3.9'] _LOGGER = logging.getLogger(__name__) -DEFAULT_NAME = 'HP ILO' +DEFAULT_NAME = "HP ILO" DEFAULT_PORT = 443 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=300) @@ -60,7 +55,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the HP ILO sensor.""" + """Set up the HP iLO sensors.""" hostname = config.get(CONF_HOST) port = config.get(CONF_PORT) login = config.get(CONF_USERNAME) @@ -73,7 +68,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): hp_ilo_data = HpIloData(hostname, port, login, password) except ValueError as error: _LOGGER.error(error) - return False + return # Initialize and add all of the sensors. devices = [] @@ -93,11 +88,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class HpIloSensor(Entity): - """Representation of a HP ILO sensor.""" + """Representation of a HP iLO sensor.""" def __init__(self, hass, hp_ilo_data, sensor_type, sensor_name, sensor_value_template, unit_of_measurement): - """Initialize the sensor.""" + """Initialize the HP iLO sensor.""" self._hass = hass self._name = sensor_name self._unit_of_measurement = unit_of_measurement @@ -111,7 +106,7 @@ def __init__(self, hass, hp_ilo_data, sensor_type, sensor_name, self._state = None self._state_attributes = None - _LOGGER.debug("Created HP ILO sensor %r", self) + _LOGGER.debug("Created HP iLO sensor %r", self) @property def name(self): @@ -130,11 +125,11 @@ def state(self): @property def device_state_attributes(self): - """Return the state attributes.""" + """Return the device state attributes.""" return self._state_attributes def update(self): - """Get the latest data from HP ILO and updates the states.""" + """Get the latest data from HP iLO and updates the states.""" # Call the API for new data. Each sensor will re-trigger this # same exact call, but that's fine. Results should be cached for # a short period of time to prevent hitting API limits. @@ -148,7 +143,7 @@ def update(self): class HpIloData: - """Gets the latest data from HP ILO.""" + """Gets the latest data from HP iLO.""" def __init__(self, host, port, login, password): """Initialize the data object.""" @@ -163,7 +158,7 @@ def __init__(self, host, port, login, password): @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): - """Get the latest data from HP ILO.""" + """Get the latest data from HP iLO.""" import hpilo try: From 112ed88d64e8176fcee41d11dfd100c8b45ed612 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Sat, 23 Mar 2019 13:21:55 +0100 Subject: [PATCH 104/605] Add homematicip cloud connection quality related attributes (#21990) --- .../components/homematicip_cloud/device.py | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/device.py b/homeassistant/components/homematicip_cloud/device.py index 9940e6960db9f2..0b815d0ec7e4d5 100644 --- a/homeassistant/components/homematicip_cloud/device.py +++ b/homeassistant/components/homematicip_cloud/device.py @@ -6,21 +6,13 @@ _LOGGER = logging.getLogger(__name__) -ATTR_CONNECTED = 'connected' -ATTR_DEVICE_ID = 'device_id' -ATTR_DEVICE_LABEL = 'device_label' -ATTR_DEVICE_RSSI = 'device_rssi' -ATTR_DUTY_CYCLE = 'duty_cycle' -ATTR_FIRMWARE_STATE = 'firmware_state' -ATTR_GROUP_TYPE = 'group_type' -ATTR_HOME_ID = 'home_id' -ATTR_HOME_NAME = 'home_name' ATTR_LOW_BATTERY = 'low_battery' ATTR_MODEL_TYPE = 'model_type' -ATTR_OPERATION_LOCK = 'operation_lock' +# RSSI HAP -> Device +ATTR_RSSI_DEVICE = 'rssi_device' +# RSSI Device -> HAP +ATTR_RSSI_PEER = 'rssi_peer' ATTR_SABOTAGE = 'sabotage' -ATTR_STATUS_UPDATE = 'status_update' -ATTR_UNREACHABLE = 'unreachable' ATTR_GROUP_MEMBER_UNREACHABLE = 'group_member_unreachable' @@ -101,7 +93,13 @@ def device_state_attributes(self): """Return the state attributes of the generic device.""" attr = {ATTR_MODEL_TYPE: self._device.modelType} if hasattr(self._device, 'lowBat') and self._device.lowBat: - attr.update({ATTR_LOW_BATTERY: self._device.lowBat}) + attr[ATTR_LOW_BATTERY] = self._device.lowBat if hasattr(self._device, 'sabotage') and self._device.sabotage: - attr.update({ATTR_SABOTAGE: self._device.sabotage}) + attr[ATTR_SABOTAGE] = self._device.sabotage + if hasattr(self._device, 'rssiDeviceValue') and \ + self._device.rssiDeviceValue: + attr[ATTR_RSSI_DEVICE] = self._device.rssiDeviceValue + if hasattr(self._device, 'rssiPeerValue') and \ + self._device.rssiPeerValue: + attr[ATTR_RSSI_PEER] = self._device.rssiPeerValue return attr From d81df1f0ae5b6373a0e3620cb78eff1a527fd866 Mon Sep 17 00:00:00 2001 From: SNoof85 Date: Sat, 23 Mar 2019 15:32:53 +0100 Subject: [PATCH 105/605] Add Freebox switch platform (#21710) * Added Freebox switch and bump aiofreepybox version * Added missing modified files * removed unused import * gen_requirements_all passed * removed unused import * Remove unused code * lint fixes * More lint fixe * Bump aiofreepybox version and API version to Freebox * Remove URL from log entry * import relative * Sort imports --- homeassistant/components/freebox/__init__.py | 6 +- homeassistant/components/freebox/switch.py | 63 ++++++++++++++++++++ requirements_all.txt | 2 +- 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/freebox/switch.py diff --git a/homeassistant/components/freebox/__init__.py b/homeassistant/components/freebox/__init__.py index 41e60d884ceb99..7accf7820f4d14 100644 --- a/homeassistant/components/freebox/__init__.py +++ b/homeassistant/components/freebox/__init__.py @@ -9,7 +9,7 @@ from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['aiofreepybox==0.0.6'] +REQUIREMENTS = ['aiofreepybox==0.0.8'] _LOGGER = logging.getLogger(__name__) @@ -60,7 +60,7 @@ async def async_setup_freebox(hass, config, host, port): } token_file = hass.config.path(FREEBOX_CONFIG_FILE) - api_version = 'v1' + api_version = 'v4' fbx = Freepybox( app_desc=app_desc, @@ -78,6 +78,8 @@ async def async_setup_freebox(hass, config, host, port): hass, 'sensor', DOMAIN, {}, config)) hass.async_create_task(async_load_platform( hass, 'device_tracker', DOMAIN, {}, config)) + hass.async_create_task(async_load_platform( + hass, 'switch', DOMAIN, {}, config)) async def close_fbx(event): """Close Freebox connection on HA Stop.""" diff --git a/homeassistant/components/freebox/switch.py b/homeassistant/components/freebox/switch.py new file mode 100644 index 00000000000000..4de194fc9023fd --- /dev/null +++ b/homeassistant/components/freebox/switch.py @@ -0,0 +1,63 @@ +"""Support for Freebox Delta, Revolution and Mini 4K.""" +import logging + +from homeassistant.components.switch import SwitchDevice + +from . import DATA_FREEBOX + +DEPENDENCIES = ['freebox'] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the switch.""" + fbx = hass.data[DATA_FREEBOX] + async_add_entities([FbxWifiSwitch(fbx)], True) + + +class FbxWifiSwitch(SwitchDevice): + """Representation of a freebox wifi switch.""" + + def __init__(self, fbx): + """Initilize the Wifi switch.""" + self._name = 'Freebox WiFi' + self._state = None + self._fbx = fbx + + @property + def name(self): + """Return the name of the switch.""" + return self._name + + @property + def is_on(self): + """Return true if device is on.""" + return self._state + + async def _async_set_state(self, enabled): + """Turn the switch on or off.""" + from aiofreepybox.exceptions import InsufficientPermissionsError + + wifi_config = {"enabled": enabled} + try: + await self._fbx.wifi.set_global_config(wifi_config) + except InsufficientPermissionsError: + _LOGGER.warning('Home Assistant does not have permissions to' + ' modify the Freebox settings. Please refer' + ' to documentation.') + + async def async_turn_on(self, **kwargs): + """Turn the switch on.""" + await self._async_set_state(True) + + async def async_turn_off(self, **kwargs): + """Turn the switch off.""" + await self._async_set_state(False) + + async def async_update(self): + """Get the state and update it.""" + datas = await self._fbx.wifi.get_global_config() + active = datas['enabled'] + self._state = bool(active) diff --git a/requirements_all.txt b/requirements_all.txt index ac20e50cd8ffba..cd74cbc0dd483d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -112,7 +112,7 @@ aiodns==1.1.1 aioesphomeapi==1.7.0 # homeassistant.components.freebox -aiofreepybox==0.0.6 +aiofreepybox==0.0.8 # homeassistant.components.yi.camera aioftp==0.12.0 From c68b621972a278878c3d11d577b5e9e5b1f8e401 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 23 Mar 2019 09:16:43 -0700 Subject: [PATCH 106/605] Google Assistant: Add camera stream trait (#22278) * Add camera stream trait * Lint --- homeassistant/components/camera/__init__.py | 13 +++++ homeassistant/components/generic/camera.py | 8 ++- .../components/google_assistant/const.py | 1 + .../components/google_assistant/smart_home.py | 21 +++++-- .../components/google_assistant/trait.py | 48 +++++++++++++++ .../google_assistant/test_smart_home.py | 58 ++++++++++++++++++- .../components/google_assistant/test_trait.py | 34 ++++++++++- 7 files changed, 176 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 95d6dba50c3769..046b6d3947c064 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -60,6 +60,7 @@ # Bitfield of features supported by the camera entity SUPPORT_ON_OFF = 1 +SUPPORT_STREAM = 2 DEFAULT_CONTENT_TYPE = 'image/jpeg' ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}' @@ -98,6 +99,18 @@ class Image: content = attr.ib(type=bytes) +@bind_hass +async def async_request_stream(hass, entity_id, fmt): + """Request a stream for a camera entity.""" + camera = _get_camera_from_entity_id(hass, entity_id) + + if not camera.stream_source: + raise HomeAssistantError("{} does not support play stream service" + .format(camera.entity_id)) + + return request_stream(hass, camera.stream_source, fmt=fmt) + + @bind_hass async def async_get_image(hass, entity_id, timeout=10): """Fetch an image from a camera entity.""" diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index c8d6721ac18af2..c9f8616f637ecc 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -18,7 +18,7 @@ HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION, CONF_VERIFY_SSL) from homeassistant.exceptions import TemplateError from homeassistant.components.camera import ( - PLATFORM_SCHEMA, DEFAULT_CONTENT_TYPE, Camera) + PLATFORM_SCHEMA, DEFAULT_CONTENT_TYPE, SUPPORT_STREAM, Camera) from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers import config_validation as cv from homeassistant.util.async_ import run_coroutine_threadsafe @@ -68,6 +68,7 @@ def __init__(self, hass, device_info): self._still_image_url.hass = hass self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE] self._frame_interval = 1 / device_info[CONF_FRAMERATE] + self._supported_features = SUPPORT_STREAM if self._stream_source else 0 self.content_type = device_info[CONF_CONTENT_TYPE] self.verify_ssl = device_info[CONF_VERIFY_SSL] @@ -85,6 +86,11 @@ def __init__(self, hass, device_info): self._last_url = None self._last_image = None + @property + def supported_features(self): + """Return supported features for this camera.""" + return self._supported_features + @property def frame_interval(self): """Return the interval between frames of the mjpeg stream.""" diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index 220ed6dd58c845..543404dd34e358 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -21,6 +21,7 @@ DEFAULT_ALLOW_UNLOCK = False PREFIX_TYPES = 'action.devices.types.' +TYPE_CAMERA = PREFIX_TYPES + 'CAMERA' TYPE_LIGHT = PREFIX_TYPES + 'LIGHT' TYPE_SWITCH = PREFIX_TYPES + 'SWITCH' TYPE_VACUUM = PREFIX_TYPES + 'VACUUM' diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index fa272c250121a5..88cbea345b1b82 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -12,6 +12,7 @@ ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, ) from homeassistant.components import ( + camera, climate, cover, fan, @@ -30,7 +31,7 @@ from . import trait from .const import ( TYPE_LIGHT, TYPE_LOCK, TYPE_SCENE, TYPE_SWITCH, TYPE_VACUUM, - TYPE_THERMOSTAT, TYPE_FAN, + TYPE_THERMOSTAT, TYPE_FAN, TYPE_CAMERA, CONF_ALIASES, CONF_ROOM_HINT, ERR_FUNCTION_NOT_SUPPORTED, ERR_PROTOCOL_ERROR, ERR_DEVICE_OFFLINE, ERR_UNKNOWN_ERROR, @@ -42,6 +43,7 @@ _LOGGER = logging.getLogger(__name__) DOMAIN_TO_GOOGLE_TYPES = { + camera.DOMAIN: TYPE_CAMERA, climate.DOMAIN: TYPE_THERMOSTAT, cover.DOMAIN: TYPE_SWITCH, fan.DOMAIN: TYPE_FAN, @@ -74,6 +76,7 @@ def __init__(self, hass, config, state): self.hass = hass self.config = config self.state = state + self._traits = None @property def entity_id(self): @@ -83,13 +86,17 @@ def entity_id(self): @callback def traits(self): """Return traits for entity.""" + if self._traits is not None: + return self._traits + state = self.state domain = state.domain features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - return [Trait(self.hass, state, self.config) - for Trait in trait.TRAITS - if Trait.supported(domain, features)] + self._traits = [Trait(self.hass, state, self.config) + for Trait in trait.TRAITS + if Trait.supported(domain, features)] + return self._traits async def sync_serialize(self): """Serialize entity for a SYNC response. @@ -202,6 +209,12 @@ def async_update(self): """Update the entity with latest info from Home Assistant.""" self.state = self.hass.states.get(self.entity_id) + if self._traits is None: + return + + for trt in self._traits: + trt.state = self.state + async def async_handle_message(hass, config, user_id, message): """Handle incoming API messages.""" diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 26d1ccc2088a0a..bd903575762ff3 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -2,6 +2,7 @@ import logging from homeassistant.components import ( + camera, cover, group, fan, @@ -35,6 +36,7 @@ _LOGGER = logging.getLogger(__name__) PREFIX_TRAITS = 'action.devices.traits.' +TRAIT_CAMERA_STREAM = PREFIX_TRAITS + 'CameraStream' TRAIT_ONOFF = PREFIX_TRAITS + 'OnOff' TRAIT_DOCK = PREFIX_TRAITS + 'Dock' TRAIT_STARTSTOP = PREFIX_TRAITS + 'StartStop' @@ -49,6 +51,7 @@ PREFIX_COMMANDS = 'action.devices.commands.' COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff' +COMMAND_GET_CAMERA_STREAM = PREFIX_COMMANDS + 'GetCameraStream' COMMAND_DOCK = PREFIX_COMMANDS + 'Dock' COMMAND_STARTSTOP = PREFIX_COMMANDS + 'StartStop' COMMAND_PAUSEUNPAUSE = PREFIX_COMMANDS + 'PauseUnpause' @@ -185,6 +188,51 @@ async def execute(self, command, data, params): }, blocking=True, context=data.context) +@register_trait +class CameraStreamTrait(_Trait): + """Trait to stream from cameras. + + https://developers.google.com/actions/smarthome/traits/camerastream + """ + + name = TRAIT_CAMERA_STREAM + commands = [ + COMMAND_GET_CAMERA_STREAM + ] + + stream_info = None + + @staticmethod + def supported(domain, features): + """Test if state is supported.""" + if domain == camera.DOMAIN: + return features & camera.SUPPORT_STREAM + + return False + + def sync_attributes(self): + """Return stream attributes for a sync request.""" + return { + 'cameraStreamSupportedProtocols': [ + "hls", + ], + 'cameraStreamNeedAuthToken': False, + 'cameraStreamNeedDrmEncryption': False, + } + + def query_attributes(self): + """Return camera stream attributes.""" + return self.stream_info or {} + + async def execute(self, command, data, params): + """Execute a get camera stream command.""" + url = await self.hass.components.camera.async_request_stream( + self.state.entity_id, 'hls') + self.stream_info = { + 'cameraStreamAccessUrl': self.hass.config.api.base_url + url + } + + @register_trait class OnOffTrait(_Trait): """Trait to offer basic on and off functionality. diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index 302e8d8674f710..cccbe0d0a9d282 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -1,10 +1,12 @@ """Test Google Smart Home.""" +from unittest.mock import patch, Mock import pytest from homeassistant.core import State, EVENT_CALL_SERVICE from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS) from homeassistant.setup import async_setup_component +from homeassistant.components import camera from homeassistant.components.climate.const import ( ATTR_MIN_TEMP, ATTR_MAX_TEMP, STATE_HEAT, SUPPORT_OPERATION_MODE ) @@ -15,7 +17,7 @@ from homeassistant.helpers import device_registry from tests.common import (mock_device_registry, mock_registry, - mock_area_registry) + mock_area_registry, mock_coro) BASIC_CONFIG = helpers.Config( should_expose=lambda state: True, @@ -557,3 +559,57 @@ async def test_query_disconnect(hass): }) assert result is None + + +async def test_trait_execute_adding_query_data(hass): + """Test a trait execute influencing query data.""" + hass.config.api = Mock(base_url='http://1.1.1.1:8123') + hass.states.async_set('camera.office', 'idle', { + 'supported_features': camera.SUPPORT_STREAM + }) + + with patch('homeassistant.components.camera.async_request_stream', + return_value=mock_coro('/api/streams/bla')): + result = await sh.async_handle_message( + hass, BASIC_CONFIG, None, + { + "requestId": REQ_ID, + "inputs": [{ + "intent": "action.devices.EXECUTE", + "payload": { + "commands": [{ + "devices": [ + {"id": "camera.office"}, + ], + "execution": [{ + "command": + "action.devices.commands.GetCameraStream", + "params": { + "StreamToChromecast": True, + "SupportedStreamProtocols": [ + "progressive_mp4", + "hls", + "dash", + "smooth_stream" + ] + } + }] + }] + } + }] + }) + + assert result == { + "requestId": REQ_ID, + "payload": { + "commands": [{ + "ids": ['camera.office'], + "status": "SUCCESS", + "states": { + "online": True, + 'cameraStreamAccessUrl': + 'http://1.1.1.1:8123/api/streams/bla', + } + }] + } + } diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 9d067f3314f865..e42e4bdc91505d 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -1,7 +1,10 @@ """Tests for the Google Assistant traits.""" +from unittest.mock import patch, Mock + import pytest from homeassistant.components import ( + camera, cover, fan, input_boolean, @@ -21,7 +24,7 @@ TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE) from homeassistant.core import State, DOMAIN as HA_DOMAIN, EVENT_CALL_SERVICE from homeassistant.util import color -from tests.common import async_mock_service +from tests.common import async_mock_service, mock_coro BASIC_CONFIG = helpers.Config( should_expose=lambda state: True, @@ -135,6 +138,35 @@ async def test_brightness_media_player(hass): } +async def test_camera_stream(hass): + """Test camera stream trait support for camera domain.""" + hass.config.api = Mock(base_url='http://1.1.1.1:8123') + assert trait.CameraStreamTrait.supported(camera.DOMAIN, + camera.SUPPORT_STREAM) + + trt = trait.CameraStreamTrait( + hass, State('camera.bla', camera.STATE_IDLE, {}), BASIC_CONFIG + ) + + assert trt.sync_attributes() == { + 'cameraStreamSupportedProtocols': [ + "hls", + ], + 'cameraStreamNeedAuthToken': False, + 'cameraStreamNeedDrmEncryption': False, + } + + assert trt.query_attributes() == {} + + with patch('homeassistant.components.camera.async_request_stream', + return_value=mock_coro('/api/streams/bla')): + await trt.execute(trait.COMMAND_GET_CAMERA_STREAM, BASIC_DATA, {}) + + assert trt.query_attributes() == { + 'cameraStreamAccessUrl': 'http://1.1.1.1:8123/api/streams/bla' + } + + async def test_onoff_group(hass): """Test OnOff trait support for group domain.""" assert trait.OnOffTrait.supported(group.DOMAIN, 0) From 16dbf9b2ea2e89e05fa583c19243d0fc76703628 Mon Sep 17 00:00:00 2001 From: Thibault Maekelbergh Date: Sat, 23 Mar 2019 20:05:08 +0100 Subject: [PATCH 107/605] Remove occupancy as it is not available in API (#22320) --- homeassistant/components/nmbs/sensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/nmbs/sensor.py b/homeassistant/components/nmbs/sensor.py index 84e187fa5a477d..15f29339087465 100644 --- a/homeassistant/components/nmbs/sensor.py +++ b/homeassistant/components/nmbs/sensor.py @@ -124,7 +124,6 @@ def device_state_attributes(self): attrs = { 'departure': "In {} minutes".format(departure), 'extra_train': int(self._attrs['isExtra']) > 0, - 'occupancy': self._attrs['occupancy']['name'], 'vehicle_id': self._attrs['vehicle'], 'monitored_station': self._station, ATTR_ATTRIBUTION: "https://api.irail.be/", From 4c4eff1d62bba3755da72e2ff873b6baa12c7429 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 23 Mar 2019 22:05:47 +0100 Subject: [PATCH 108/605] Update file header (#22318) * Update file header * Fix indent * Fix lint issue --- homeassistant/components/hyperion/__init__.py | 2 +- homeassistant/components/hyperion/light.py | 45 ++++++++----------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/hyperion/__init__.py b/homeassistant/components/hyperion/__init__.py index 2e78b777f7d8a5..60a0a2d3210a78 100644 --- a/homeassistant/components/hyperion/__init__.py +++ b/homeassistant/components/hyperion/__init__.py @@ -1 +1 @@ -"""The hyperion component.""" +"""The Hyperion component.""" diff --git a/homeassistant/components/hyperion/light.py b/homeassistant/components/hyperion/light.py index 16be7d45825116..1fc5f78d0e85be 100644 --- a/homeassistant/components/hyperion/light.py +++ b/homeassistant/components/hyperion/light.py @@ -1,9 +1,4 @@ -""" -Support for Hyperion remotes. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.hyperion/ -""" +"""Support for Hyperion remotes.""" import json import logging import socket @@ -11,9 +6,9 @@ import voluptuous as vol from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_EFFECT, SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, SUPPORT_EFFECT, Light, PLATFORM_SCHEMA) -from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_NAME) + ATTR_BRIGHTNESS, ATTR_EFFECT, ATTR_HS_COLOR, PLATFORM_SCHEMA, + SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_EFFECT, Light) +from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util @@ -47,34 +42,32 @@ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_DEFAULT_COLOR, default=DEFAULT_COLOR): - vol.All(list, vol.Length(min=3, max=3), - [vol.All(vol.Coerce(int), vol.Range(min=0, max=255))]), + vol.All(list, vol.Length(min=3, max=3), + [vol.All(vol.Coerce(int), vol.Range(min=0, max=255))]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PRIORITY, default=DEFAULT_PRIORITY): cv.positive_int, - vol.Optional(CONF_HDMI_PRIORITY, - default=DEFAULT_HDMI_PRIORITY): cv.positive_int, - vol.Optional(CONF_EFFECT_LIST, - default=DEFAULT_EFFECT_LIST): vol.All(cv.ensure_list, - [cv.string]), + vol.Optional(CONF_HDMI_PRIORITY, default=DEFAULT_HDMI_PRIORITY): + cv.positive_int, + vol.Optional(CONF_EFFECT_LIST, default=DEFAULT_EFFECT_LIST): + vol.All(cv.ensure_list, [cv.string]), }) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Hyperion server remote.""" - host = config.get(CONF_HOST) - port = config.get(CONF_PORT) - priority = config.get(CONF_PRIORITY) - hdmi_priority = config.get(CONF_HDMI_PRIORITY) - default_color = config.get(CONF_DEFAULT_COLOR) - effect_list = config.get(CONF_EFFECT_LIST) + name = config[CONF_NAME] + host = config[CONF_HOST] + port = config[CONF_PORT] + priority = config[CONF_PRIORITY] + hdmi_priority = config[CONF_HDMI_PRIORITY] + default_color = config[CONF_DEFAULT_COLOR] + effect_list = config[CONF_EFFECT_LIST] - device = Hyperion(config.get(CONF_NAME), host, port, priority, - default_color, hdmi_priority, effect_list) + device = Hyperion( + name, host, port, priority, default_color, hdmi_priority, effect_list) if device.setup(): add_entities([device]) - return True - return False class Hyperion(Light): From 1ddc249989e415688ea01b011bd6b06e1dacea27 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 23 Mar 2019 20:22:35 -0700 Subject: [PATCH 109/605] Consolidate more platforms (#22308) * Consolidate final platforms * Fix some tests * Fix more tests * Fix more tests --- .../components/demo/alarm_control_panel.py | 4 +- .../{calendar/demo.py => demo/calendar.py} | 2 +- .../{camera/demo.py => demo/camera.py} | 2 +- .../{climate/demo.py => demo/climate.py} | 4 +- .../{cover/demo.py => demo/cover.py} | 2 +- .../components/{camera => demo}/demo_0.jpg | Bin .../components/{camera => demo}/demo_1.jpg | Bin .../components/{camera => demo}/demo_2.jpg | Bin .../components/{camera => demo}/demo_3.jpg | Bin .../components/{fan/demo.py => demo/fan.py} | 2 +- .../demo.py => demo/geo_location.py} | 2 +- .../{light/demo.py => demo/light.py} | 2 +- .../components/{lock/demo.py => demo/lock.py} | 2 +- .../demo.py => demo/media_player.py} | 4 +- .../{vacuum/demo.py => demo/vacuum.py} | 2 +- .../demo.py => demo/water_heater.py} | 2 +- .../components/generic_thermostat/__init__.py | 1 + .../climate.py} | 4 +- .../{cover/group.py => group/cover.py} | 2 +- .../{light/group.py => group/light.py} | 2 +- .../alarm_control_panel.py} | 0 .../alarm_control_panel.py} | 0 .../{light/switch.py => switch/light.py} | 2 +- script/gen_requirements_all.py | 2 +- tests/components/camera/test_init.py | 8 +-- .../test_demo.py => demo/test_calendar.py} | 0 .../test_demo.py => demo/test_camera.py} | 2 +- .../test_demo.py => demo/test_climate.py} | 0 .../test_demo.py => demo/test_cover.py} | 0 .../{fan/test_demo.py => demo/test_fan.py} | 0 .../test_geo_location.py} | 2 +- .../test_demo.py => demo/test_light.py} | 0 .../{lock/test_demo.py => demo/test_lock.py} | 0 .../test_media_player.py} | 2 +- .../test_demo.py => demo/test_vacuum.py} | 2 +- .../test_water_heater.py} | 0 .../facebox/test_image_processing.py | 2 +- .../test_climate.py} | 0 .../google_assistant/test_smart_home.py | 2 +- .../test_group.py => group/test_cover.py} | 2 +- .../test_group.py => group/test_light.py} | 2 +- .../components/image_processing/test_init.py | 2 +- .../test_alarm_control_panel.py} | 60 ++++++++-------- .../test_alarm_control_panel.py} | 66 +++++++++--------- .../test_switch.py => switch/test_light.py} | 0 45 files changed, 98 insertions(+), 97 deletions(-) rename homeassistant/components/{calendar/demo.py => demo/calendar.py} (97%) rename homeassistant/components/{camera/demo.py => demo/camera.py} (97%) rename homeassistant/components/{climate/demo.py => demo/climate.py} (98%) rename homeassistant/components/{cover/demo.py => demo/cover.py} (99%) rename homeassistant/components/{camera => demo}/demo_0.jpg (100%) rename homeassistant/components/{camera => demo}/demo_1.jpg (100%) rename homeassistant/components/{camera => demo}/demo_2.jpg (100%) rename homeassistant/components/{camera => demo}/demo_3.jpg (100%) rename homeassistant/components/{fan/demo.py => demo/fan.py} (98%) rename homeassistant/components/{geo_location/demo.py => demo/geo_location.py} (98%) rename homeassistant/components/{light/demo.py => demo/light.py} (98%) rename homeassistant/components/{lock/demo.py => demo/lock.py} (96%) rename homeassistant/components/{media_player/demo.py => demo/media_player.py} (99%) rename homeassistant/components/{vacuum/demo.py => demo/vacuum.py} (99%) rename homeassistant/components/{water_heater/demo.py => demo/water_heater.py} (98%) create mode 100644 homeassistant/components/generic_thermostat/__init__.py rename homeassistant/components/{climate/generic_thermostat.py => generic_thermostat/climate.py} (99%) rename homeassistant/components/{cover/group.py => group/cover.py} (99%) rename homeassistant/components/{light/group.py => group/light.py} (99%) rename homeassistant/components/{alarm_control_panel/manual.py => manual/alarm_control_panel.py} (100%) rename homeassistant/components/{alarm_control_panel/manual_mqtt.py => manual_mqtt/alarm_control_panel.py} (100%) rename homeassistant/components/{light/switch.py => switch/light.py} (98%) rename tests/components/{calendar/test_demo.py => demo/test_calendar.py} (100%) rename tests/components/{camera/test_demo.py => demo/test_camera.py} (97%) rename tests/components/{climate/test_demo.py => demo/test_climate.py} (100%) rename tests/components/{cover/test_demo.py => demo/test_cover.py} (100%) rename tests/components/{fan/test_demo.py => demo/test_fan.py} (100%) rename tests/components/{geo_location/test_demo.py => demo/test_geo_location.py} (97%) rename tests/components/{light/test_demo.py => demo/test_light.py} (100%) rename tests/components/{lock/test_demo.py => demo/test_lock.py} (100%) rename tests/components/{media_player/test_demo.py => demo/test_media_player.py} (99%) rename tests/components/{vacuum/test_demo.py => demo/test_vacuum.py} (99%) rename tests/components/{water_heater/test_demo.py => demo/test_water_heater.py} (100%) rename tests/components/{climate/test_generic_thermostat.py => generic_thermostat/test_climate.py} (100%) rename tests/components/{cover/test_group.py => group/test_cover.py} (99%) rename tests/components/{light/test_group.py => group/test_light.py} (99%) rename tests/components/{alarm_control_panel/test_manual.py => manual/test_alarm_control_panel.py} (95%) rename tests/components/{alarm_control_panel/test_manual_mqtt.py => manual_mqtt/test_alarm_control_panel.py} (95%) rename tests/components/{light/test_switch.py => switch/test_light.py} (100%) diff --git a/homeassistant/components/demo/alarm_control_panel.py b/homeassistant/components/demo/alarm_control_panel.py index fb4dccc1c865e7..4d317f52daacd0 100644 --- a/homeassistant/components/demo/alarm_control_panel.py +++ b/homeassistant/components/demo/alarm_control_panel.py @@ -5,7 +5,7 @@ https://home-assistant.io/components/demo/ """ import datetime -from homeassistant.components.alarm_control_panel import manual +from homeassistant.components.manual.alarm_control_panel import ManualAlarm from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, @@ -17,7 +17,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Demo alarm control panel platform.""" async_add_entities([ - manual.ManualAlarm(hass, 'Alarm', '1234', None, False, { + ManualAlarm(hass, 'Alarm', '1234', None, False, { STATE_ALARM_ARMED_AWAY: { CONF_DELAY_TIME: datetime.timedelta(seconds=0), CONF_PENDING_TIME: datetime.timedelta(seconds=5), diff --git a/homeassistant/components/calendar/demo.py b/homeassistant/components/demo/calendar.py similarity index 97% rename from homeassistant/components/calendar/demo.py rename to homeassistant/components/demo/calendar.py index bd5724ca455815..720b4cc51809be 100644 --- a/homeassistant/components/calendar/demo.py +++ b/homeassistant/components/demo/calendar.py @@ -9,7 +9,7 @@ from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME import homeassistant.util.dt as dt_util -from . import CalendarEventDevice, get_date +from homeassistant.components.calendar import CalendarEventDevice, get_date def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/demo/camera.py similarity index 97% rename from homeassistant/components/camera/demo.py rename to homeassistant/components/demo/camera.py index f9be3f47c358fd..34a0894ac60ba8 100644 --- a/homeassistant/components/camera/demo.py +++ b/homeassistant/components/demo/camera.py @@ -7,7 +7,7 @@ import logging import os -from . import SUPPORT_ON_OFF, Camera +from homeassistant.components.camera import SUPPORT_ON_OFF, Camera _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/climate/demo.py b/homeassistant/components/demo/climate.py similarity index 98% rename from homeassistant/components/climate/demo.py rename to homeassistant/components/demo/climate.py index 2dd31c1b20d8ca..b1dd1b0ba456cc 100644 --- a/homeassistant/components/climate/demo.py +++ b/homeassistant/components/demo/climate.py @@ -6,8 +6,8 @@ """ from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT -from . import ClimateDevice -from .const import ( +from homeassistant.components.climate import ClimateDevice +from homeassistant.components.climate.const import ( ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_HUMIDITY, diff --git a/homeassistant/components/cover/demo.py b/homeassistant/components/demo/cover.py similarity index 99% rename from homeassistant/components/cover/demo.py rename to homeassistant/components/demo/cover.py index 1f31cecc9966c7..ddcf07fd5e5127 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/demo/cover.py @@ -6,7 +6,7 @@ """ from homeassistant.helpers.event import track_utc_time_change -from . import ( +from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, SUPPORT_CLOSE, SUPPORT_OPEN, CoverDevice) diff --git a/homeassistant/components/camera/demo_0.jpg b/homeassistant/components/demo/demo_0.jpg similarity index 100% rename from homeassistant/components/camera/demo_0.jpg rename to homeassistant/components/demo/demo_0.jpg diff --git a/homeassistant/components/camera/demo_1.jpg b/homeassistant/components/demo/demo_1.jpg similarity index 100% rename from homeassistant/components/camera/demo_1.jpg rename to homeassistant/components/demo/demo_1.jpg diff --git a/homeassistant/components/camera/demo_2.jpg b/homeassistant/components/demo/demo_2.jpg similarity index 100% rename from homeassistant/components/camera/demo_2.jpg rename to homeassistant/components/demo/demo_2.jpg diff --git a/homeassistant/components/camera/demo_3.jpg b/homeassistant/components/demo/demo_3.jpg similarity index 100% rename from homeassistant/components/camera/demo_3.jpg rename to homeassistant/components/demo/demo_3.jpg diff --git a/homeassistant/components/fan/demo.py b/homeassistant/components/demo/fan.py similarity index 98% rename from homeassistant/components/fan/demo.py rename to homeassistant/components/demo/fan.py index e67fbef650e6c7..53729795f71bf1 100644 --- a/homeassistant/components/fan/demo.py +++ b/homeassistant/components/demo/fan.py @@ -6,7 +6,7 @@ """ from homeassistant.const import STATE_OFF -from . import ( +from homeassistant.components.fan import ( SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity) diff --git a/homeassistant/components/geo_location/demo.py b/homeassistant/components/demo/geo_location.py similarity index 98% rename from homeassistant/components/geo_location/demo.py rename to homeassistant/components/demo/geo_location.py index d63280ce609a7f..6b91faac92f368 100644 --- a/homeassistant/components/geo_location/demo.py +++ b/homeassistant/components/demo/geo_location.py @@ -7,7 +7,7 @@ from homeassistant.helpers.event import track_time_interval -from . import GeolocationEvent +from homeassistant.components.geo_location import GeolocationEvent _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/light/demo.py b/homeassistant/components/demo/light.py similarity index 98% rename from homeassistant/components/light/demo.py rename to homeassistant/components/demo/light.py index d9affb03db3756..a5b22108e8130a 100644 --- a/homeassistant/components/light/demo.py +++ b/homeassistant/components/demo/light.py @@ -6,7 +6,7 @@ """ import random -from . import ( +from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, ATTR_WHITE_VALUE, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_WHITE_VALUE, Light) diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/demo/lock.py similarity index 96% rename from homeassistant/components/lock/demo.py rename to homeassistant/components/demo/lock.py index 94a67fb87f6d0c..03935c4f603bcc 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/demo/lock.py @@ -6,7 +6,7 @@ """ from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED -from . import SUPPORT_OPEN, LockDevice +from homeassistant.components.lock import SUPPORT_OPEN, LockDevice def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/media_player/demo.py b/homeassistant/components/demo/media_player.py similarity index 99% rename from homeassistant/components/media_player/demo.py rename to homeassistant/components/demo/media_player.py index 070701f6322be7..33d2b98d225ece 100644 --- a/homeassistant/components/media_player/demo.py +++ b/homeassistant/components/demo/media_player.py @@ -7,8 +7,8 @@ from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING import homeassistant.util.dt as dt_util -from . import MediaPlayerDevice -from .const import ( +from homeassistant.components.media_player import MediaPlayerDevice +from homeassistant.components.media_player.const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOUND_MODE, diff --git a/homeassistant/components/vacuum/demo.py b/homeassistant/components/demo/vacuum.py similarity index 99% rename from homeassistant/components/vacuum/demo.py rename to homeassistant/components/demo/vacuum.py index 2a19337c0db9be..5ec7030f56cf9d 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/demo/vacuum.py @@ -6,7 +6,7 @@ """ import logging -from . import ( +from homeassistant.components.vacuum import ( ATTR_CLEANED_AREA, STATE_CLEANING, STATE_DOCKED, STATE_IDLE, STATE_PAUSED, STATE_RETURNING, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, diff --git a/homeassistant/components/water_heater/demo.py b/homeassistant/components/demo/water_heater.py similarity index 98% rename from homeassistant/components/water_heater/demo.py rename to homeassistant/components/demo/water_heater.py index 37ae535cdfc824..6ee17bf0088d6d 100644 --- a/homeassistant/components/water_heater/demo.py +++ b/homeassistant/components/demo/water_heater.py @@ -1,7 +1,7 @@ """Demo platform that offers a fake water heater device.""" from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT -from . import ( +from homeassistant.components.water_heater import ( SUPPORT_AWAY_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, WaterHeaterDevice) diff --git a/homeassistant/components/generic_thermostat/__init__.py b/homeassistant/components/generic_thermostat/__init__.py new file mode 100644 index 00000000000000..d0bc392e4f4f22 --- /dev/null +++ b/homeassistant/components/generic_thermostat/__init__.py @@ -0,0 +1 @@ +"""The generic_thermostat component.""" diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/generic_thermostat/climate.py similarity index 99% rename from homeassistant/components/climate/generic_thermostat.py rename to homeassistant/components/generic_thermostat/climate.py index af0a3eea6ab09b..1eb0f8e79db484 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -20,8 +20,8 @@ async_track_state_change, async_track_time_interval) from homeassistant.helpers.restore_state import RestoreEntity -from . import PLATFORM_SCHEMA, ClimateDevice -from .const import ( +from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice +from homeassistant.components.climate.const import ( ATTR_AWAY_MODE, ATTR_OPERATION_MODE, STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_AWAY_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) diff --git a/homeassistant/components/cover/group.py b/homeassistant/components/group/cover.py similarity index 99% rename from homeassistant/components/cover/group.py rename to homeassistant/components/group/cover.py index aba57284da8f58..c1825211433716 100644 --- a/homeassistant/components/cover/group.py +++ b/homeassistant/components/group/cover.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_state_change -from . import ( +from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_CURRENT_TILT_POSITION, ATTR_POSITION, ATTR_TILT_POSITION, DOMAIN, PLATFORM_SCHEMA, SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, SERVICE_OPEN_COVER_TILT, diff --git a/homeassistant/components/light/group.py b/homeassistant/components/group/light.py similarity index 99% rename from homeassistant/components/light/group.py rename to homeassistant/components/group/light.py index 7e9d11d3a02329..c37c5cc4e8e6ee 100644 --- a/homeassistant/components/light/group.py +++ b/homeassistant/components/group/light.py @@ -20,7 +20,7 @@ from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from . import ( +from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_EFFECT_LIST, ATTR_FLASH, ATTR_HS_COLOR, ATTR_MAX_MIREDS, ATTR_MIN_MIREDS, ATTR_TRANSITION, ATTR_WHITE_VALUE, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/manual/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/manual.py rename to homeassistant/components/manual/alarm_control_panel.py diff --git a/homeassistant/components/alarm_control_panel/manual_mqtt.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py similarity index 100% rename from homeassistant/components/alarm_control_panel/manual_mqtt.py rename to homeassistant/components/manual_mqtt/alarm_control_panel.py diff --git a/homeassistant/components/light/switch.py b/homeassistant/components/switch/light.py similarity index 98% rename from homeassistant/components/light/switch.py rename to homeassistant/components/switch/light.py index 4b4f33133490d3..64f8779e4ab611 100644 --- a/homeassistant/components/light/switch.py +++ b/homeassistant/components/switch/light.py @@ -16,7 +16,7 @@ from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from . import PLATFORM_SCHEMA, Light +from homeassistant.components.light import PLATFORM_SCHEMA, Light _LOGGER = logging.getLogger(__name__) diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 6912c83f770826..fa6a8429ff3218 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -222,7 +222,7 @@ def gather_modules(): if fnmatch.fnmatch(package, pattern): break else: - print("{}: {}".format(package, err)) + print("{}: {}".format(package.replace('.', '/') + '.py', err)) errors.append(package) continue diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 840e30161f3a1c..0359d14df63596 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -28,7 +28,7 @@ def mock_camera(hass): } })) - with patch('homeassistant.components.camera.demo.DemoCamera.camera_image', + with patch('homeassistant.components.demo.camera.DemoCamera.camera_image', return_value=b'Test'): yield @@ -92,7 +92,7 @@ def teardown_method(self): """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.camera.demo.DemoCamera.camera_image', + @patch('homeassistant.components.demo.camera.DemoCamera.camera_image', autospec=True, return_value=b'Test') def test_get_image_from_camera(self, mock_camera): """Grab an image from camera entity.""" @@ -199,7 +199,7 @@ async def test_webocket_camera_stream(hass, hass_ws_client, hass_client, with patch('homeassistant.components.camera.request_stream', return_value='http://home.assistant/playlist.m3u8' ) as mock_request_stream, \ - patch('homeassistant.components.camera.demo.DemoCamera.stream_source', + patch('homeassistant.components.demo.camera.DemoCamera.stream_source', new_callable=PropertyMock) as mock_stream_source: mock_stream_source.return_value = generate_h264_video() # Request playlist through WebSocket @@ -241,7 +241,7 @@ async def test_handle_play_stream_service(hass, mock_camera, mock_stream): } with patch('homeassistant.components.camera.request_stream' ) as mock_request_stream, \ - patch('homeassistant.components.camera.demo.DemoCamera.stream_source', + patch('homeassistant.components.demo.camera.DemoCamera.stream_source', new_callable=PropertyMock) as mock_stream_source: mock_stream_source.return_value = generate_h264_video() # Call service diff --git a/tests/components/calendar/test_demo.py b/tests/components/demo/test_calendar.py similarity index 100% rename from tests/components/calendar/test_demo.py rename to tests/components/demo/test_calendar.py diff --git a/tests/components/camera/test_demo.py b/tests/components/demo/test_camera.py similarity index 97% rename from tests/components/camera/test_demo.py rename to tests/components/demo/test_camera.py index f6e2513380cb64..6329a36543fb30 100644 --- a/tests/components/camera/test_demo.py +++ b/tests/components/demo/test_camera.py @@ -27,7 +27,7 @@ async def test_init_state_is_streaming(hass, demo_camera): assert demo_camera.state == STATE_STREAMING mock_on_img = mock_open(read_data=b'ON') - with patch('homeassistant.components.camera.demo.open', mock_on_img, + with patch('homeassistant.components.demo.camera.open', mock_on_img, create=True): image = await camera.async_get_image(hass, demo_camera.entity_id) assert mock_on_img.called diff --git a/tests/components/climate/test_demo.py b/tests/components/demo/test_climate.py similarity index 100% rename from tests/components/climate/test_demo.py rename to tests/components/demo/test_climate.py diff --git a/tests/components/cover/test_demo.py b/tests/components/demo/test_cover.py similarity index 100% rename from tests/components/cover/test_demo.py rename to tests/components/demo/test_cover.py diff --git a/tests/components/fan/test_demo.py b/tests/components/demo/test_fan.py similarity index 100% rename from tests/components/fan/test_demo.py rename to tests/components/demo/test_fan.py diff --git a/tests/components/geo_location/test_demo.py b/tests/components/demo/test_geo_location.py similarity index 97% rename from tests/components/geo_location/test_demo.py rename to tests/components/demo/test_geo_location.py index d1c11fe55bf67b..5a46ca998396c9 100644 --- a/tests/components/geo_location/test_demo.py +++ b/tests/components/demo/test_geo_location.py @@ -3,7 +3,7 @@ from unittest.mock import patch from homeassistant.components import geo_location -from homeassistant.components.geo_location.demo import \ +from homeassistant.components.demo.geo_location import \ NUMBER_OF_DEMO_DEVICES, DEFAULT_UNIT_OF_MEASUREMENT, \ DEFAULT_UPDATE_INTERVAL from homeassistant.setup import setup_component diff --git a/tests/components/light/test_demo.py b/tests/components/demo/test_light.py similarity index 100% rename from tests/components/light/test_demo.py rename to tests/components/demo/test_light.py diff --git a/tests/components/lock/test_demo.py b/tests/components/demo/test_lock.py similarity index 100% rename from tests/components/lock/test_demo.py rename to tests/components/demo/test_lock.py diff --git a/tests/components/media_player/test_demo.py b/tests/components/demo/test_media_player.py similarity index 99% rename from tests/components/media_player/test_demo.py rename to tests/components/demo/test_media_player.py index 8cbe0a594d2940..83acf8be601bd5 100644 --- a/tests/components/media_player/test_demo.py +++ b/tests/components/demo/test_media_player.py @@ -184,7 +184,7 @@ def test_prev_next_track(self): state = self.hass.states.get(ent_id) assert 1 == state.attributes.get('media_episode') - @patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.' + @patch('homeassistant.components.demo.media_player.DemoYoutubePlayer.' 'media_seek', autospec=True) def test_play_media(self, mock_seek): """Test play_media .""" diff --git a/tests/components/vacuum/test_demo.py b/tests/components/demo/test_vacuum.py similarity index 99% rename from tests/components/vacuum/test_demo.py rename to tests/components/demo/test_vacuum.py index e0b560bbb48d40..523fe17f8248b0 100644 --- a/tests/components/vacuum/test_demo.py +++ b/tests/components/demo/test_vacuum.py @@ -9,7 +9,7 @@ SERVICE_SEND_COMMAND, SERVICE_SET_FAN_SPEED, STATE_DOCKED, STATE_CLEANING, STATE_PAUSED, STATE_IDLE, STATE_RETURNING) -from homeassistant.components.vacuum.demo import ( +from homeassistant.components.demo.vacuum import ( DEMO_VACUUM_BASIC, DEMO_VACUUM_COMPLETE, DEMO_VACUUM_MINIMAL, DEMO_VACUUM_MOST, DEMO_VACUUM_NONE, DEMO_VACUUM_STATE, FAN_SPEEDS) from homeassistant.const import ( diff --git a/tests/components/water_heater/test_demo.py b/tests/components/demo/test_water_heater.py similarity index 100% rename from tests/components/water_heater/test_demo.py rename to tests/components/demo/test_water_heater.py diff --git a/tests/components/facebox/test_image_processing.py b/tests/components/facebox/test_image_processing.py index a4e7890f3fa272..971b6830ae172e 100644 --- a/tests/components/facebox/test_image_processing.py +++ b/tests/components/facebox/test_image_processing.py @@ -89,7 +89,7 @@ def mock_isfile(): @pytest.fixture def mock_image(): """Return a mock camera image.""" - with patch('homeassistant.components.camera.demo.DemoCamera.camera_image', + with patch('homeassistant.components.demo.camera.DemoCamera.camera_image', return_value=b'Test') as image: yield image diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/generic_thermostat/test_climate.py similarity index 100% rename from tests/components/climate/test_generic_thermostat.py rename to tests/components/generic_thermostat/test_climate.py diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index cccbe0d0a9d282..bc59cc8ff2d7c0 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -13,7 +13,7 @@ from homeassistant.components.google_assistant import ( const, trait, helpers, smart_home as sh, EVENT_COMMAND_RECEIVED, EVENT_QUERY_RECEIVED, EVENT_SYNC_RECEIVED) -from homeassistant.components.light.demo import DemoLight +from homeassistant.components.demo.light import DemoLight from homeassistant.helpers import device_registry from tests.common import (mock_device_registry, mock_registry, diff --git a/tests/components/cover/test_group.py b/tests/components/group/test_cover.py similarity index 99% rename from tests/components/cover/test_group.py rename to tests/components/group/test_cover.py index 2211c8c77bc55a..04e8f9c964d1bd 100644 --- a/tests/components/cover/test_group.py +++ b/tests/components/group/test_cover.py @@ -6,7 +6,7 @@ from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_CURRENT_TILT_POSITION, ATTR_POSITION, ATTR_TILT_POSITION, DOMAIN) -from homeassistant.components.cover.group import DEFAULT_NAME +from homeassistant.components.group.cover import DEFAULT_NAME from homeassistant.const import ( ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, CONF_ENTITIES, diff --git a/tests/components/light/test_group.py b/tests/components/group/test_light.py similarity index 99% rename from tests/components/light/test_group.py rename to tests/components/group/test_light.py index 472bdf42385908..51580e503bd58c 100644 --- a/tests/components/light/test_group.py +++ b/tests/components/group/test_light.py @@ -3,7 +3,7 @@ import asynctest -from homeassistant.components.light import group +import homeassistant.components.group.light as group from homeassistant.setup import async_setup_component from tests.components.light import common diff --git a/tests/components/image_processing/test_init.py b/tests/components/image_processing/test_init.py index 8bec3bd71f56d0..86f5f820be3691 100644 --- a/tests/components/image_processing/test_init.py +++ b/tests/components/image_processing/test_init.py @@ -80,7 +80,7 @@ def teardown_method(self): """Stop everything that was started.""" self.hass.stop() - @patch('homeassistant.components.camera.demo.DemoCamera.camera_image', + @patch('homeassistant.components.demo.camera.DemoCamera.camera_image', autospec=True, return_value=b'Test') def test_get_image_from_camera(self, mock_camera): """Grab an image from camera entity.""" diff --git a/tests/components/alarm_control_panel/test_manual.py b/tests/components/manual/test_alarm_control_panel.py similarity index 95% rename from tests/components/alarm_control_panel/test_manual.py rename to tests/components/manual/test_alarm_control_panel.py index 14326b3f32d8c6..a6e59af64d5bd1 100644 --- a/tests/components/alarm_control_panel/test_manual.py +++ b/tests/components/manual/test_alarm_control_panel.py @@ -76,7 +76,7 @@ async def test_arm_home_with_pending(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_ARMED_HOME future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -184,7 +184,7 @@ async def test_arm_away_with_pending(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_ARMED_AWAY future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -269,7 +269,7 @@ async def test_arm_night_with_pending(hass): STATE_ALARM_ARMED_NIGHT future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -332,7 +332,7 @@ async def test_trigger_no_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=60) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -374,7 +374,7 @@ async def test_trigger_with_delay(hass): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -458,7 +458,7 @@ async def test_trigger_with_pending(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -467,7 +467,7 @@ async def test_trigger_with_pending(hass): assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -512,7 +512,7 @@ async def test_trigger_with_unused_specific_delay(hass): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -557,7 +557,7 @@ async def test_trigger_with_specific_delay(hass): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -601,7 +601,7 @@ async def test_trigger_with_pending_and_delay(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -611,7 +611,7 @@ async def test_trigger_with_pending_and_delay(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -658,7 +658,7 @@ async def test_trigger_with_pending_and_specific_delay(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -668,7 +668,7 @@ async def test_trigger_with_pending_and_specific_delay(hass): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -699,7 +699,7 @@ async def test_armed_home_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -730,7 +730,7 @@ async def test_armed_away_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -761,7 +761,7 @@ async def test_armed_night_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -794,7 +794,7 @@ async def test_trigger_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -803,7 +803,7 @@ async def test_trigger_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -836,7 +836,7 @@ async def test_trigger_with_disarm_after_trigger(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -899,7 +899,7 @@ async def test_trigger_with_unused_zero_specific_trigger_time(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -934,7 +934,7 @@ async def test_trigger_with_specific_trigger_time(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -973,7 +973,7 @@ async def test_trigger_with_no_disarm_after_trigger(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1012,7 +1012,7 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1027,7 +1027,7 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1065,7 +1065,7 @@ async def test_disarm_while_pending_trigger(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1104,7 +1104,7 @@ async def test_disarm_during_trigger_with_invalid_code(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1202,7 +1202,7 @@ async def test_arm_custom_bypass_with_pending(hass): STATE_ALARM_ARMED_CUSTOM_BYPASS future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1257,7 +1257,7 @@ async def test_armed_custom_bypass_with_specific_pending(hass): hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1311,7 +1311,7 @@ async def test_arm_away_after_disabled_disarmed(hass): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -1330,7 +1330,7 @@ async def test_arm_away_after_disabled_disarmed(hass): state.attributes['post_pending_state'] future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual.' + with patch(('homeassistant.components.manual.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() diff --git a/tests/components/alarm_control_panel/test_manual_mqtt.py b/tests/components/manual_mqtt/test_alarm_control_panel.py similarity index 95% rename from tests/components/alarm_control_panel/test_manual_mqtt.py rename to tests/components/manual_mqtt/test_alarm_control_panel.py index 3d063f8a34ae85..f5558331bce60c 100644 --- a/tests/components/alarm_control_panel/test_manual_mqtt.py +++ b/tests/components/manual_mqtt/test_alarm_control_panel.py @@ -106,7 +106,7 @@ def test_arm_home_with_pending(self): assert state.attributes['post_pending_state'] == STATE_ALARM_ARMED_HOME future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -221,7 +221,7 @@ def test_arm_away_with_pending(self): assert state.attributes['post_pending_state'] == STATE_ALARM_ARMED_AWAY future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -309,7 +309,7 @@ def test_arm_night_with_pending(self): STATE_ALARM_ARMED_NIGHT future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -374,7 +374,7 @@ def test_trigger_no_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=60) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -417,7 +417,7 @@ def test_trigger_with_delay(self): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -504,7 +504,7 @@ def test_trigger_with_pending(self): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -513,7 +513,7 @@ def test_trigger_with_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -547,7 +547,7 @@ def test_trigger_with_disarm_after_trigger(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -612,7 +612,7 @@ def test_trigger_with_unused_zero_specific_trigger_time(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -648,7 +648,7 @@ def test_trigger_with_specific_trigger_time(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -688,7 +688,7 @@ def test_back_to_back_trigger_with_no_disarm_after_trigger(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -703,7 +703,7 @@ def test_back_to_back_trigger_with_no_disarm_after_trigger(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -742,7 +742,7 @@ def test_disarm_while_pending_trigger(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -782,7 +782,7 @@ def test_disarm_during_trigger_with_invalid_code(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -828,7 +828,7 @@ def test_trigger_with_unused_specific_delay(self): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -874,7 +874,7 @@ def test_trigger_with_specific_delay(self): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -919,7 +919,7 @@ def test_trigger_with_pending_and_delay(self): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -929,7 +929,7 @@ def test_trigger_with_pending_and_delay(self): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -977,7 +977,7 @@ def test_trigger_with_pending_and_specific_delay(self): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -987,7 +987,7 @@ def test_trigger_with_pending_and_specific_delay(self): assert state.attributes['post_pending_state'] == STATE_ALARM_TRIGGERED future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1019,7 +1019,7 @@ def test_armed_home_with_specific_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1051,7 +1051,7 @@ def test_armed_away_with_specific_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1083,7 +1083,7 @@ def test_armed_night_with_specific_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1117,7 +1117,7 @@ def test_trigger_with_specific_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=2) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1126,7 +1126,7 @@ def test_trigger_with_specific_pending(self): self.hass.states.get(entity_id).state future = dt_util.utcnow() + timedelta(seconds=5) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1181,7 +1181,7 @@ def test_arm_away_after_disabled_disarmed(self): state.attributes['post_pending_state'] future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1200,7 +1200,7 @@ def test_arm_away_after_disabled_disarmed(self): state.attributes['post_pending_state'] future += timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1275,7 +1275,7 @@ def test_arm_home_via_command_topic(self): # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1309,7 +1309,7 @@ def test_arm_away_via_command_topic(self): # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1343,7 +1343,7 @@ def test_arm_night_via_command_topic(self): # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1409,7 +1409,7 @@ def test_state_changes_are_published_to_mqtt(self): self.mock_publish.async_publish.reset_mock() # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1425,7 +1425,7 @@ def test_state_changes_are_published_to_mqtt(self): self.mock_publish.async_publish.reset_mock() # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() @@ -1441,7 +1441,7 @@ def test_state_changes_are_published_to_mqtt(self): self.mock_publish.async_publish.reset_mock() # Fast-forward a little bit future = dt_util.utcnow() + timedelta(seconds=1) - with patch(('homeassistant.components.alarm_control_panel.manual_mqtt.' + with patch(('homeassistant.components.manual_mqtt.alarm_control_panel.' 'dt_util.utcnow'), return_value=future): fire_time_changed(self.hass, future) self.hass.block_till_done() diff --git a/tests/components/light/test_switch.py b/tests/components/switch/test_light.py similarity index 100% rename from tests/components/light/test_switch.py rename to tests/components/switch/test_light.py From 49b92b5349b74c6e6a527cf72c54553224486bd4 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 23 Mar 2019 23:49:44 -0400 Subject: [PATCH 110/605] fix where PLATFORM_SCHEMA gets pulled from (#22334) --- homeassistant/components/familyhub/camera.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/familyhub/camera.py b/homeassistant/components/familyhub/camera.py index f3dd8b6d0c9166..e14bd9f10987ea 100644 --- a/homeassistant/components/familyhub/camera.py +++ b/homeassistant/components/familyhub/camera.py @@ -8,8 +8,7 @@ import voluptuous as vol -from homeassistant.components.camera import Camera -from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.components.camera import Camera, PLATFORM_SCHEMA from homeassistant.const import CONF_IP_ADDRESS, CONF_NAME from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv From c5f4aa046626d41e003174e1b2809a92ec0a3ae6 Mon Sep 17 00:00:00 2001 From: uchagani Date: Sun, 24 Mar 2019 02:06:55 -0400 Subject: [PATCH 111/605] show which component is causing translation errors (#22340) --- homeassistant/helpers/translation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index e1c5895b89ad44..63a6421d5f66ef 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -62,7 +62,7 @@ def component_translation_file(hass: HomeAssistantType, component: str, # It's a platform parts = component.split('.', 1) module = get_platform(hass, *parts) - assert module is not None + assert module is not None, component # Either within HA or custom_components # Either light/hue.py or hue/light.py From 71ebc4f5946f42e5e5802b3401e718c12b170aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Marc=20Simard?= Date: Sun, 24 Mar 2019 07:15:30 -0400 Subject: [PATCH 112/605] Define GTFS sensor as a timestamp device class (#21053) --- homeassistant/components/gtfs/sensor.py | 57 +++++++++++++------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index eec08be093f038..25b352c14545df 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -12,9 +12,10 @@ import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_NAME +from homeassistant.const import CONF_NAME, DEVICE_CLASS_TIMESTAMP from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv +import homeassistant.util.dt as dt_util REQUIREMENTS = ['pygtfs==0.1.5'] @@ -40,9 +41,6 @@ 7: 'mdi:stairs', } -DATE_FORMAT = '%Y-%m-%d' -TIME_FORMAT = '%Y-%m-%d %H:%M:%S' - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ORIGIN): cv.string, vol.Required(CONF_DESTINATION): cv.string, @@ -60,7 +58,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): now = datetime.datetime.now() + offset day_name = now.strftime('%A').lower() now_str = now.strftime('%H:%M:%S') - today = now.strftime(DATE_FORMAT) + today = now.strftime(dt_util.DATE_STR_FORMAT) from sqlalchemy.sql import text @@ -117,28 +115,28 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): origin_arrival = now if item['origin_arrival_time'] > item['origin_depart_time']: origin_arrival -= datetime.timedelta(days=1) - origin_arrival_time = '{} {}'.format(origin_arrival.strftime(DATE_FORMAT), - item['origin_arrival_time']) + origin_arrival_time = '{} {}'.format( + origin_arrival.strftime(dt_util.DATE_STR_FORMAT), + item['origin_arrival_time']) origin_depart_time = '{} {}'.format(today, item['origin_depart_time']) dest_arrival = now if item['dest_arrival_time'] < item['origin_depart_time']: dest_arrival += datetime.timedelta(days=1) - dest_arrival_time = '{} {}'.format(dest_arrival.strftime(DATE_FORMAT), - item['dest_arrival_time']) + dest_arrival_time = '{} {}'.format( + dest_arrival.strftime(dt_util.DATE_STR_FORMAT), + item['dest_arrival_time']) dest_depart = dest_arrival if item['dest_depart_time'] < item['dest_arrival_time']: dest_depart += datetime.timedelta(days=1) - dest_depart_time = '{} {}'.format(dest_depart.strftime(DATE_FORMAT), - item['dest_depart_time']) - - depart_time = datetime.datetime.strptime(origin_depart_time, TIME_FORMAT) - arrival_time = datetime.datetime.strptime(dest_arrival_time, TIME_FORMAT) + dest_depart_time = '{} {}'.format( + dest_depart.strftime(dt_util.DATE_STR_FORMAT), + item['dest_depart_time']) - seconds_until = (depart_time - datetime.datetime.now()).total_seconds() - minutes_until = int(seconds_until / 60) + depart_time = dt_util.parse_datetime(origin_depart_time) + arrival_time = dt_util.parse_datetime(dest_arrival_time) route = sched.routes_by_id(item['route_id'])[0] @@ -168,11 +166,9 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): 'route': route, 'agency': sched.agencies_by_id(route.agency_id)[0], 'origin_station': origin_station, - 'departure_time': depart_time, 'destination_station': destination_station, + 'departure_time': depart_time, 'arrival_time': arrival_time, - 'seconds_until_departure': seconds_until, - 'minutes_until_departure': minutes_until, 'origin_stop_time': origin_stop_time_dict, 'destination_stop_time': destination_stop_time_dict } @@ -222,7 +218,6 @@ def __init__(self, pygtfs, name, origin, destination, offset): self._custom_name = name self._icon = ICON self._name = '' - self._unit_of_measurement = 'min' self._state = None self._attributes = {} self.lock = threading.Lock() @@ -238,11 +233,6 @@ def state(self): """Return the state of the sensor.""" return self._state - @property - def unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement - @property def device_state_attributes(self): """Return the state attributes.""" @@ -253,6 +243,11 @@ def icon(self): """Icon to use in the frontend, if any.""" return self._icon + @property + def device_class(self): + """Return the class of this device.""" + return DEVICE_CLASS_TIMESTAMP + def update(self): """Get the latest data from GTFS and update the states.""" with self.lock: @@ -265,7 +260,12 @@ def update(self): self._name = (self._custom_name or DEFAULT_NAME) return - self._state = self._departure['minutes_until_departure'] + # Define the state as a UTC timestamp with ISO 8601 format. + arrival_time = dt_util.as_utc( + self._departure['arrival_time']).isoformat() + departure_time = dt_util.as_utc( + self._departure['departure_time']).isoformat() + self._state = departure_time origin_station = self._departure['origin_station'] destination_station = self._departure['destination_station'] @@ -281,12 +281,13 @@ def update(self): origin_station.stop_id, destination_station.stop_id)) + self._icon = ICONS.get(route.route_type, ICON) + # Build attributes self._attributes = {} + self._attributes['arrival'] = arrival_time self._attributes['offset'] = self._offset.seconds / 60 - self._icon = ICONS.get(route.route_type, ICON) - def dict_for_table(resource): """Return a dict for the SQLAlchemy resource given.""" return dict((col, getattr(resource, col)) From 9214934d47871575d6b235dd547fc1ccab79c029 Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 24 Mar 2019 13:01:12 +0100 Subject: [PATCH 113/605] Move yeelight into component (#21593) --- .../components/discovery/__init__.py | 3 +- homeassistant/components/light/services.yaml | 26 -- homeassistant/components/yeelight/__init__.py | 358 +++++++++++++++++- homeassistant/components/yeelight/light.py | 306 ++++----------- .../components/yeelight/services.yaml | 25 ++ requirements_all.txt | 2 +- 6 files changed, 450 insertions(+), 270 deletions(-) create mode 100644 homeassistant/components/yeelight/services.yaml diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index d4816213f50085..1fb727642bc50d 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -46,6 +46,7 @@ SERVICE_SABNZBD = 'sabnzbd' SERVICE_SAMSUNG_PRINTER = 'samsung_printer' SERVICE_TELLDUSLIVE = 'tellstick' +SERVICE_YEELIGHT = 'yeelight' SERVICE_WEMO = 'belkin_wemo' SERVICE_WINK = 'wink' SERVICE_XIAOMI_GW = 'xiaomi_gw' @@ -79,6 +80,7 @@ SERVICE_KONNECTED: ('konnected', None), SERVICE_OCTOPRINT: ('octoprint', None), SERVICE_FREEBOX: ('freebox', None), + SERVICE_YEELIGHT: ('yeelight', None), 'panasonic_viera': ('media_player', 'panasonic_viera'), 'plex_mediaserver': ('media_player', 'plex'), 'yamaha': ('media_player', 'yamaha'), @@ -86,7 +88,6 @@ 'directv': ('media_player', 'directv'), 'denonavr': ('media_player', 'denonavr'), 'samsung_tv': ('media_player', 'samsungtv'), - 'yeelight': ('light', 'yeelight'), 'frontier_silicon': ('media_player', 'frontier_silicon'), 'openhome': ('media_player', 'openhome'), 'harmony': ('remote', 'harmony'), diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index a2863482477a9a..cdf82e97429ad2 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -178,29 +178,3 @@ xiaomi_miio_set_delayed_turn_off: time_period: description: Time period for the delayed turn off. example: "5, '0:05', {'minutes': 5}" - -yeelight_set_mode: - description: Set a operation mode. - fields: - entity_id: - description: Name of the light entity. - example: 'light.yeelight' - mode: - description: Operation mode. Valid values are 'last', 'normal', 'rgb', 'hsv', 'color_flow', 'moonlight'. - example: 'moonlight' - -yeelight_start_flow: - description: Start a custom flow, using transitions from https://yeelight.readthedocs.io/en/stable/yeelight.html#flow-objects - fields: - entity_id: - description: Name of the light entity. - example: 'light.yeelight' - count: - description: The number of times to run this flow (0 to run forever). - example: 0 - action: - description: The action to take after the flow stops. Can be 'recover', 'stay', 'off'. (default 'recover') - example: 'stay' - transitions: - description: Array of transitions, for desired effect. Examples https://yeelight.readthedocs.io/en/stable/flow.html - example: '[{ "TemperatureTransition": [1900, 1000, 80] }, { "TemperatureTransition": [1900, 1000, 10] }]' diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index d8c1f23bcbb2a5..32e3c5f69e3b6f 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -1 +1,357 @@ -"""The yeelight component.""" +""" +Support for Xiaomi Yeelight Wifi color bulb. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/yeelight/ +""" +import logging +from datetime import timedelta + +import voluptuous as vol +from homeassistant.components.discovery import SERVICE_YEELIGHT +from homeassistant.const import CONF_DEVICES, CONF_NAME, CONF_SCAN_INTERVAL, \ + CONF_HOST, ATTR_ENTITY_ID, CONF_LIGHTS +from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN +from homeassistant.helpers import discovery +from homeassistant.helpers.discovery import load_platform +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.service import extract_entity_ids + +REQUIREMENTS = ['yeelight==0.4.3'] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "yeelight" +DATA_YEELIGHT = DOMAIN +DATA_UPDATED = '{}_data_updated'.format(DOMAIN) + +DEFAULT_NAME = 'Yeelight' +DEFAULT_TRANSITION = 350 + +CONF_MODEL = 'model' +CONF_TRANSITION = 'transition' +CONF_SAVE_ON_CHANGE = 'save_on_change' +CONF_MODE_MUSIC = 'use_music_mode' +CONF_FLOW_PARAMS = 'flow_params' +CONF_CUSTOM_EFFECTS = 'custom_effects' + +ATTR_MODE = 'mode' +ATTR_COUNT = 'count' +ATTR_ACTION = 'action' +ATTR_TRANSITIONS = 'transitions' + +ACTION_RECOVER = 'recover' +ACTION_STAY = 'stay' +ACTION_OFF = 'off' + +MODE_MOONLIGHT = 'moonlight' +MODE_DAYLIGHT = 'normal' + +SCAN_INTERVAL = timedelta(seconds=30) + +YEELIGHT_RGB_TRANSITION = 'RGBTransition' +YEELIGHT_HSV_TRANSACTION = 'HSVTransition' +YEELIGHT_TEMPERATURE_TRANSACTION = 'TemperatureTransition' +YEELIGHT_SLEEP_TRANSACTION = 'SleepTransition' + +SERVICE_SET_MODE = 'set_mode' +SERVICE_START_FLOW = 'start_flow' + +YEELIGHT_FLOW_TRANSITION_SCHEMA = { + vol.Optional(ATTR_COUNT, default=0): cv.positive_int, + vol.Optional(ATTR_ACTION, default=ACTION_RECOVER): + vol.Any(ACTION_RECOVER, ACTION_OFF, ACTION_STAY), + vol.Required(ATTR_TRANSITIONS): [{ + vol.Exclusive(YEELIGHT_RGB_TRANSITION, CONF_TRANSITION): + vol.All(cv.ensure_list, [cv.positive_int]), + vol.Exclusive(YEELIGHT_HSV_TRANSACTION, CONF_TRANSITION): + vol.All(cv.ensure_list, [cv.positive_int]), + vol.Exclusive(YEELIGHT_TEMPERATURE_TRANSACTION, CONF_TRANSITION): + vol.All(cv.ensure_list, [cv.positive_int]), + vol.Exclusive(YEELIGHT_SLEEP_TRANSACTION, CONF_TRANSITION): + vol.All(cv.ensure_list, [cv.positive_int]), + }] +} + +DEVICE_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_TRANSITION, default=DEFAULT_TRANSITION): cv.positive_int, + vol.Optional(CONF_MODE_MUSIC, default=False): cv.boolean, + vol.Optional(CONF_SAVE_ON_CHANGE, default=False): cv.boolean, + vol.Optional(CONF_MODEL): cv.string, +}) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, + vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): + cv.time_period, + vol.Optional(CONF_CUSTOM_EFFECTS): [{ + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_FLOW_PARAMS): YEELIGHT_FLOW_TRANSITION_SCHEMA + }] + }), +}, extra=vol.ALLOW_EXTRA) + +YEELIGHT_SERVICE_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_ids, +}) + +NIGHTLIGHT_SUPPORTED_MODELS = [ + "ceiling3", + 'ceiling4' +] + +UPDATE_REQUEST_PROPERTIES = [ + "power", + "bright", + "ct", + "rgb", + "hue", + "sat", + "color_mode", + "flowing", + "music_on", + "nl_br", + "active_mode", +] + + +def _transitions_config_parser(transitions): + """Parse transitions config into initialized objects.""" + import yeelight + + transition_objects = [] + for transition_config in transitions: + transition, params = list(transition_config.items())[0] + transition_objects.append(getattr(yeelight, transition)(*params)) + + return transition_objects + + +def _parse_custom_effects(effects_config): + import yeelight + + effects = {} + for config in effects_config: + params = config[CONF_FLOW_PARAMS] + action = yeelight.Flow.actions[params[ATTR_ACTION]] + transitions = _transitions_config_parser( + params[ATTR_TRANSITIONS]) + + effects[config[CONF_NAME]] = { + ATTR_COUNT: params[ATTR_COUNT], + ATTR_ACTION: action, + ATTR_TRANSITIONS: transitions + } + + return effects + + +def setup(hass, config): + """Set up the Yeelight bulbs.""" + from yeelight.enums import PowerMode + + conf = config[DOMAIN] + yeelight_data = hass.data[DATA_YEELIGHT] = { + CONF_DEVICES: {}, + CONF_LIGHTS: {}, + } + + def device_discovered(service, info): + _LOGGER.debug("Adding autodetected %s", info['hostname']) + + device_type = info['device_type'] + + name = "yeelight_%s_%s" % (device_type, + info['properties']['mac']) + ipaddr = info[CONF_HOST] + device_config = DEVICE_SCHEMA({ + CONF_NAME: name, + CONF_MODEL: device_type + }) + + _setup_device(hass, config, ipaddr, device_config) + + discovery.listen(hass, SERVICE_YEELIGHT, device_discovered) + + def async_update(event): + for device in yeelight_data[CONF_DEVICES].values(): + device.update() + + async_track_time_interval( + hass, async_update, conf[CONF_SCAN_INTERVAL] + ) + + def service_handler(service): + """Dispatch service calls to target entities.""" + params = {key: value for key, value in service.data.items() + if key != ATTR_ENTITY_ID} + + entity_ids = extract_entity_ids(hass, service) + target_devices = [dev.device for dev in + yeelight_data[CONF_LIGHTS].values() + if dev.entity_id in entity_ids] + + for target_device in target_devices: + if service.service == SERVICE_SET_MODE: + target_device.set_mode(**params) + elif service.service == SERVICE_START_FLOW: + params[ATTR_TRANSITIONS] = \ + _transitions_config_parser(params[ATTR_TRANSITIONS]) + target_device.start_flow(**params) + + service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_MODE): + vol.In([mode.name.lower() for mode in PowerMode]) + }) + hass.services.register( + DOMAIN, SERVICE_SET_MODE, service_handler, + schema=service_schema_set_mode) + + service_schema_start_flow = YEELIGHT_SERVICE_SCHEMA.extend( + YEELIGHT_FLOW_TRANSITION_SCHEMA + ) + hass.services.register( + DOMAIN, SERVICE_START_FLOW, service_handler, + schema=service_schema_start_flow) + + for ipaddr, device_config in conf[CONF_DEVICES].items(): + _LOGGER.debug("Adding configured %s", device_config[CONF_NAME]) + _setup_device(hass, config, ipaddr, device_config) + + return True + + +def _setup_device(hass, hass_config, ipaddr, device_config): + devices = hass.data[DATA_YEELIGHT][CONF_DEVICES] + + if ipaddr in devices: + return + + device = YeelightDevice(hass, ipaddr, device_config) + + devices[ipaddr] = device + + platform_config = device_config.copy() + platform_config[CONF_HOST] = ipaddr + platform_config[CONF_CUSTOM_EFFECTS] = _parse_custom_effects( + hass_config[DATA_YEELIGHT].get(CONF_CUSTOM_EFFECTS, {}) + ) + + load_platform(hass, LIGHT_DOMAIN, DOMAIN, platform_config, hass_config) + + +class YeelightDevice: + """Represents single Yeelight device.""" + + def __init__(self, hass, ipaddr, config): + """Initialize device.""" + self._hass = hass + self._config = config + self._ipaddr = ipaddr + self._name = config.get(CONF_NAME) + self._model = config.get(CONF_MODEL) + self._bulb_device = None + + @property + def bulb(self): + """Return bulb device.""" + import yeelight + if self._bulb_device is None: + try: + self._bulb_device = yeelight.Bulb(self._ipaddr, + model=self._model) + # force init for type + self._update_properties() + + except yeelight.BulbException as ex: + _LOGGER.error("Failed to connect to bulb %s, %s: %s", + self._ipaddr, self._name, ex) + + return self._bulb_device + + def _update_properties(self): + self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) + + @property + def name(self): + """Return the name of the device if any.""" + return self._name + + @property + def config(self): + """Return device config.""" + return self._config + + @property + def ipaddr(self): + """Return ip address.""" + return self._ipaddr + + @property + def is_nightlight_enabled(self) -> bool: + """Return true / false if nightlight is currently enabled.""" + if self._bulb_device is None: + return False + + return self.bulb.last_properties.get('active_mode') == '1' + + def turn_on(self, duration=DEFAULT_TRANSITION): + """Turn on device.""" + import yeelight + + try: + self._bulb_device.turn_on(duration=duration) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to turn the bulb on: %s", ex) + return + + self.update() + + def turn_off(self, duration=DEFAULT_TRANSITION): + """Turn off device.""" + import yeelight + + try: + self._bulb_device.turn_off(duration=duration) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to turn the bulb on: %s", ex) + return + + self.update() + + def update(self): + """Read new properties from the device.""" + if not self.bulb: + return + + self._update_properties() + dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) + + def set_mode(self, mode: str): + """Set a power mode.""" + import yeelight + + try: + self.bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to set the power mode: %s", ex) + + self.update() + + def start_flow(self, transitions, count=0, action=ACTION_RECOVER): + """Start flow.""" + import yeelight + + try: + flow = yeelight.Flow( + count=count, + action=yeelight.Flow.actions[action], + transitions=transitions) + + self.bulb.start_flow(flow) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to set effect: %s", ex) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 18a0bf750a1351..8c7a94d3020659 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -1,99 +1,26 @@ -""" -Support for Xiaomi Yeelight Wifi color bulb. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.yeelight/ -""" +"""Light platform support for yeelight.""" import logging -import voluptuous as vol - +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util.color import ( color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) -from homeassistant.const import CONF_DEVICES, CONF_NAME +from homeassistant.const import CONF_HOST, CONF_DEVICES, CONF_LIGHTS +from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_COLOR_TEMP, ATTR_FLASH, FLASH_SHORT, FLASH_LONG, ATTR_EFFECT, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP, SUPPORT_FLASH, - SUPPORT_EFFECT, Light, PLATFORM_SCHEMA, ATTR_ENTITY_ID, DOMAIN) -import homeassistant.helpers.config_validation as cv + SUPPORT_EFFECT, Light) import homeassistant.util.color as color_util +from homeassistant.components.yeelight import ( + CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, + CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED) -REQUIREMENTS = ['yeelight==0.4.3'] +DEPENDENCIES = ['yeelight'] _LOGGER = logging.getLogger(__name__) -LEGACY_DEVICE_TYPE_MAP = { - 'color1': 'rgb', - 'mono1': 'white', - 'strip1': 'strip', - 'bslamp1': 'bedside', - 'ceiling1': 'ceiling', -} - -DEFAULT_NAME = 'Yeelight' -DEFAULT_TRANSITION = 350 - -CONF_MODEL = 'model' -CONF_TRANSITION = 'transition' -CONF_SAVE_ON_CHANGE = 'save_on_change' -CONF_MODE_MUSIC = 'use_music_mode' -CONF_CUSTOM_EFFECTS = 'custom_effects' -CONF_FLOW_PARAMS = 'flow_params' - -DATA_KEY = 'light.yeelight' - -ATTR_MODE = 'mode' -ATTR_COUNT = 'count' -ATTR_ACTION = 'action' -ATTR_TRANSITIONS = 'transitions' - -ACTION_RECOVER = 'recover' -ACTION_STAY = 'stay' -ACTION_OFF = 'off' - -YEELIGHT_RGB_TRANSITION = 'RGBTransition' -YEELIGHT_HSV_TRANSACTION = 'HSVTransition' -YEELIGHT_TEMPERATURE_TRANSACTION = 'TemperatureTransition' -YEELIGHT_SLEEP_TRANSACTION = 'SleepTransition' - -YEELIGHT_SERVICE_SCHEMA = vol.Schema({ - vol.Required(ATTR_ENTITY_ID): cv.entity_ids, -}) - -YEELIGHT_FLOW_TRANSITION_SCHEMA = { - vol.Optional(ATTR_COUNT, default=0): cv.positive_int, - vol.Optional(ATTR_ACTION, default=ACTION_RECOVER): - vol.Any(ACTION_RECOVER, ACTION_OFF, ACTION_STAY), - vol.Required(ATTR_TRANSITIONS): [{ - vol.Exclusive(YEELIGHT_RGB_TRANSITION, CONF_TRANSITION): - vol.All(cv.ensure_list, [cv.positive_int]), - vol.Exclusive(YEELIGHT_HSV_TRANSACTION, CONF_TRANSITION): - vol.All(cv.ensure_list, [cv.positive_int]), - vol.Exclusive(YEELIGHT_TEMPERATURE_TRANSACTION, CONF_TRANSITION): - vol.All(cv.ensure_list, [cv.positive_int]), - vol.Exclusive(YEELIGHT_SLEEP_TRANSACTION, CONF_TRANSITION): - vol.All(cv.ensure_list, [cv.positive_int]), - }] -} - -DEVICE_SCHEMA = vol.Schema({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_TRANSITION, default=DEFAULT_TRANSITION): cv.positive_int, - vol.Optional(CONF_MODE_MUSIC, default=False): cv.boolean, - vol.Optional(CONF_SAVE_ON_CHANGE, default=False): cv.boolean, - vol.Optional(CONF_MODEL): cv.string, -}) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, - vol.Optional(CONF_CUSTOM_EFFECTS): [{ - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_FLOW_PARAMS): YEELIGHT_FLOW_TRANSITION_SCHEMA - }] -}) - SUPPORT_YEELIGHT = (SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_FLASH) @@ -143,9 +70,6 @@ EFFECT_TWITTER, EFFECT_STOP] -SERVICE_SET_MODE = 'yeelight_set_mode' -SERVICE_START_FLOW = 'yeelight_start_flow' - def _cmd(func): """Define a wrapper to catch exceptions from the bulb.""" @@ -160,117 +84,39 @@ def _wrap(self, *args, **kwargs): return _wrap -def _parse_custom_effects(effects_config): - import yeelight - - effects = {} - for config in effects_config: - params = config[CONF_FLOW_PARAMS] - action = yeelight.Flow.actions[params[ATTR_ACTION]] - transitions = YeelightLight.transitions_config_parser( - params[ATTR_TRANSITIONS]) - - effects[config[CONF_NAME]] = { - ATTR_COUNT: params[ATTR_COUNT], - ATTR_ACTION: action, - ATTR_TRANSITIONS: transitions - } - - return effects - - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yeelight bulbs.""" - from yeelight.enums import PowerMode - - if DATA_KEY not in hass.data: - hass.data[DATA_KEY] = {} + if not discovery_info: + return - lights = [] - if discovery_info is not None: - _LOGGER.debug("Adding autodetected %s", discovery_info['hostname']) + yeelight_data = hass.data[DATA_YEELIGHT] + ipaddr = discovery_info[CONF_HOST] + device = yeelight_data[CONF_DEVICES][ipaddr] + _LOGGER.debug("Adding %s", device.name) - device_type = discovery_info['device_type'] - legacy_device_type = LEGACY_DEVICE_TYPE_MAP.get(device_type, - device_type) + custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] + light = YeelightLight(device, custom_effects=custom_effects) - # Not using hostname, as it seems to vary. - name = "yeelight_%s_%s" % (legacy_device_type, - discovery_info['properties']['mac']) - device = {'name': name, 'ipaddr': discovery_info['host']} - - light = YeelightLight(device, DEVICE_SCHEMA({CONF_MODEL: device_type})) - lights.append(light) - hass.data[DATA_KEY][name] = light - else: - for ipaddr, device_config in config[CONF_DEVICES].items(): - name = device_config[CONF_NAME] - _LOGGER.debug("Adding configured %s", name) - - device = {'name': name, 'ipaddr': ipaddr} - - if CONF_CUSTOM_EFFECTS in config: - custom_effects = \ - _parse_custom_effects(config[CONF_CUSTOM_EFFECTS]) - else: - custom_effects = None - - light = YeelightLight(device, device_config, - custom_effects=custom_effects) - lights.append(light) - hass.data[DATA_KEY][name] = light - - add_entities(lights, True) - - def service_handler(service): - """Dispatch service calls to target entities.""" - params = {key: value for key, value in service.data.items() - if key != ATTR_ENTITY_ID} - entity_ids = service.data.get(ATTR_ENTITY_ID) - target_devices = [dev for dev in hass.data[DATA_KEY].values() - if dev.entity_id in entity_ids] - - for target_device in target_devices: - if service.service == SERVICE_SET_MODE: - target_device.set_mode(**params) - elif service.service == SERVICE_START_FLOW: - target_device.start_flow(**params) - - service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({ - vol.Required(ATTR_MODE): - vol.In([mode.name.lower() for mode in PowerMode]) - }) - hass.services.register( - DOMAIN, SERVICE_SET_MODE, service_handler, - schema=service_schema_set_mode) - - service_schema_start_flow = YEELIGHT_SERVICE_SCHEMA.extend( - YEELIGHT_FLOW_TRANSITION_SCHEMA - ) - hass.services.register( - DOMAIN, SERVICE_START_FLOW, service_handler, - schema=service_schema_start_flow) + yeelight_data[CONF_LIGHTS][ipaddr] = light + add_entities([light], True) class YeelightLight(Light): """Representation of a Yeelight light.""" - def __init__(self, device, config, custom_effects=None): + def __init__(self, device, custom_effects=None): """Initialize the Yeelight light.""" - self.config = config - self._name = device['name'] - self._ipaddr = device['ipaddr'] + self.config = device.config + self._device = device self._supported_features = SUPPORT_YEELIGHT self._available = False - self._bulb_device = None self._brightness = None self._color_temp = None self._is_on = None self._hs = None - self._model = config.get('model') self._min_mireds = None self._max_mireds = None @@ -279,6 +125,22 @@ def __init__(self, device, config, custom_effects=None): else: self._custom_effects = {} + @callback + def _schedule_immediate_update(self, ipaddr): + if ipaddr == self.device.ipaddr: + self.async_schedule_update_ha_state(True) + + async def async_added_to_hass(self): + """Handle entity which will be added.""" + async_dispatcher_connect( + self.hass, DATA_UPDATED, self._schedule_immediate_update + ) + + @property + def should_poll(self): + """No polling needed.""" + return False + @property def available(self) -> bool: """Return if bulb is available.""" @@ -302,7 +164,7 @@ def color_temp(self) -> int: @property def name(self) -> str: """Return the name of the device if any.""" - return self._name + return self.device.name @property def is_on(self) -> bool: @@ -363,27 +225,26 @@ def hs_color(self) -> tuple: @property def _properties(self) -> dict: - if self._bulb_device is None: + if self._bulb is None: return {} - return self._bulb_device.last_properties + return self._bulb.last_properties + + @property + def device(self): + """Return yeelight device.""" + return self._device # F821: https://github.com/PyCQA/pyflakes/issues/373 @property def _bulb(self) -> 'yeelight.Bulb': # noqa: F821 - import yeelight - if self._bulb_device is None: - try: - self._bulb_device = yeelight.Bulb(self._ipaddr, - model=self._model) - self._bulb_device.get_properties() # force init for type + bulb = self.device.bulb - self._available = True - except yeelight.BulbException as ex: - self._available = False - _LOGGER.error("Failed to connect to bulb %s, %s: %s", - self._ipaddr, self._name, ex) + if bulb: + self._available = True + return bulb - return self._bulb_device + self._available = False + return None def set_music_mode(self, mode) -> None: """Set the music mode on or off.""" @@ -396,12 +257,13 @@ def update(self) -> None: """Update properties from the bulb.""" import yeelight try: - self._bulb.get_properties() - - if self._bulb_device.bulb_type == yeelight.BulbType.Color: + if self._bulb.bulb_type == yeelight.BulbType.Color: self._supported_features = SUPPORT_YEELIGHT_RGB - elif self._bulb_device.bulb_type == yeelight.BulbType.WhiteTemp: - self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP + elif self._bulb.bulb_type == yeelight.BulbType.WhiteTemp: + if self._device.is_nightlight_enabled: + self._supported_features = SUPPORT_YEELIGHT + else: + self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP if self._min_mireds is None: model_specs = self._bulb.get_model_specs() @@ -412,7 +274,11 @@ def update(self) -> None: self._is_on = self._properties.get('power') == 'on' - bright = self._properties.get('bright', None) + if self._device.is_nightlight_enabled: + bright = self._properties.get('nl_br', None) + else: + bright = self._properties.get('bright', None) + if bright: self._brightness = round(255 * (int(bright) / 100)) @@ -552,11 +418,7 @@ def turn_on(self, **kwargs) -> None: if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - try: - self._bulb.turn_on(duration=duration) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb on: %s", ex) - return + self.device.turn_on(duration=duration) if self.config[CONF_MODE_MUSIC] and not self._bulb.music_mode: try: @@ -588,46 +450,8 @@ def turn_on(self, **kwargs) -> None: def turn_off(self, **kwargs) -> None: """Turn off.""" - import yeelight duration = int(self.config[CONF_TRANSITION]) # in ms if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - try: - self._bulb.turn_off(duration=duration) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb off: %s", ex) - - def set_mode(self, mode: str): - """Set a power mode.""" - import yeelight - try: - self._bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) - self.async_schedule_update_ha_state(True) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to set the power mode: %s", ex) - - @staticmethod - def transitions_config_parser(transitions): - """Parse transitions config into initialized objects.""" - import yeelight - - transition_objects = [] - for transition_config in transitions: - transition, params = list(transition_config.items())[0] - transition_objects.append(getattr(yeelight, transition)(*params)) - return transition_objects - - def start_flow(self, transitions, count=0, action=ACTION_RECOVER): - """Start flow.""" - import yeelight - - try: - flow = yeelight.Flow( - count=count, - action=yeelight.Flow.actions[action], - transitions=self.transitions_config_parser(transitions)) - - self._bulb.start_flow(flow) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to set effect: %s", ex) + self.device.turn_off(duration=duration) diff --git a/homeassistant/components/yeelight/services.yaml b/homeassistant/components/yeelight/services.yaml new file mode 100644 index 00000000000000..14dcfb27a4d54a --- /dev/null +++ b/homeassistant/components/yeelight/services.yaml @@ -0,0 +1,25 @@ +set_mode: + description: Set a operation mode. + fields: + entity_id: + description: Name of the light entity. + example: 'light.yeelight' + mode: + description: Operation mode. Valid values are 'last', 'normal', 'rgb', 'hsv', 'color_flow', 'moonlight'. + example: 'moonlight' + +start_flow: + description: Start a custom flow, using transitions from https://yeelight.readthedocs.io/en/stable/yeelight.html#flow-objects + fields: + entity_id: + description: Name of the light entity. + example: 'light.yeelight' + count: + description: The number of times to run this flow (0 to run forever). + example: 0 + action: + description: The action to take after the flow stops. Can be 'recover', 'stay', 'off'. (default 'recover') + example: 'stay' + transitions: + description: Array of transitions, for desired effect. Examples https://yeelight.readthedocs.io/en/stable/flow.html + example: '[{ "TemperatureTransition": [1900, 1000, 80] }, { "TemperatureTransition": [1900, 1000, 10] }]' diff --git a/requirements_all.txt b/requirements_all.txt index cd74cbc0dd483d..ea91ef5e9f40c8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1813,7 +1813,7 @@ yahooweather==0.10 # homeassistant.components.yale_smart_alarm.alarm_control_panel yalesmartalarmclient==0.1.6 -# homeassistant.components.yeelight.light +# homeassistant.components.yeelight yeelight==0.4.3 # homeassistant.components.yeelightsunflower.light From 6988fe783cd780c742825894d00eb056d3c7e622 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 24 Mar 2019 16:16:50 +0100 Subject: [PATCH 114/605] Axis config flow (#18543) * Initial draft * Add tests for init Fix hound comments * Add tests for device Change parameter handling to make device easier to test * Remove superfluous functionality per Martins request * Fix hound comments * Embedded platforms * Fix device import * Config flow retry * Options default values will be set automatically to options in config entry before component can be used * Clean up init Add populate options Fix small issues in config flow Add tests covering init * Improve device tests * Add config flow tests * Fix hound comments * Rebase miss * Initial tests for binary sensors * Clean up More binary sensor tests * Hound comments * Add camera tests * Fix initial state of sensors * Bump dependency to v17 * Fix pylint and flake8 * Fix comments --- .coveragerc | 1 - .../components/axis/.translations/en.json | 26 ++ homeassistant/components/axis/__init__.py | 268 +++------------ .../components/axis/binary_sensor.py | 83 ++--- homeassistant/components/axis/camera.py | 69 ++-- homeassistant/components/axis/config_flow.py | 202 +++++++++++ homeassistant/components/axis/const.py | 12 + homeassistant/components/axis/device.py | 127 +++++++ homeassistant/components/axis/errors.py | 22 ++ homeassistant/components/axis/services.yaml | 15 - homeassistant/components/axis/strings.json | 26 ++ .../components/discovery/__init__.py | 2 +- homeassistant/config_entries.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/axis/__init__.py | 1 + tests/components/axis/test_binary_sensor.py | 102 ++++++ tests/components/axis/test_camera.py | 73 ++++ tests/components/axis/test_config_flow.py | 319 ++++++++++++++++++ tests/components/axis/test_device.py | 152 +++++++++ tests/components/axis/test_init.py | 97 ++++++ 22 files changed, 1284 insertions(+), 320 deletions(-) create mode 100644 homeassistant/components/axis/.translations/en.json create mode 100644 homeassistant/components/axis/config_flow.py create mode 100644 homeassistant/components/axis/const.py create mode 100644 homeassistant/components/axis/device.py create mode 100644 homeassistant/components/axis/errors.py delete mode 100644 homeassistant/components/axis/services.yaml create mode 100644 homeassistant/components/axis/strings.json create mode 100644 tests/components/axis/__init__.py create mode 100644 tests/components/axis/test_binary_sensor.py create mode 100644 tests/components/axis/test_camera.py create mode 100644 tests/components/axis/test_config_flow.py create mode 100644 tests/components/axis/test_device.py create mode 100644 tests/components/axis/test_init.py diff --git a/.coveragerc b/.coveragerc index 67b0c9f76a939e..42e7d84dc099bf 100644 --- a/.coveragerc +++ b/.coveragerc @@ -36,7 +36,6 @@ omit = homeassistant/components/arlo/* homeassistant/components/asterisk_mbox/* homeassistant/components/august/* - homeassistant/components/axis/* homeassistant/components/bbb_gpio/* homeassistant/components/arest/binary_sensor.py homeassistant/components/concord232/binary_sensor.py diff --git a/homeassistant/components/axis/.translations/en.json b/homeassistant/components/axis/.translations/en.json new file mode 100644 index 00000000000000..3c528dfbb16112 --- /dev/null +++ b/homeassistant/components/axis/.translations/en.json @@ -0,0 +1,26 @@ +{ + "config": { + "title": "Axis device", + "step": { + "user": { + "title": "Set up Axis device", + "data": { + "host": "Host", + "username": "Username", + "password": "Password", + "port": "Port" + } + } + }, + "error": { + "already_configured": "Device is already configured", + "device_unavailable": "Device is not available", + "faulty_credentials": "Bad user credentials" + }, + "abort": { + "already_configured": "Device is already configured", + "bad_config_file": "Bad data from config file", + "link_local_address": "Link local addresses are not supported" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/__init__.py b/homeassistant/components/axis/__init__.py index df723272a7acd7..324c2cf369e8a5 100644 --- a/homeassistant/components/axis/__init__.py +++ b/homeassistant/components/axis/__init__.py @@ -1,262 +1,76 @@ """Support for Axis devices.""" -import logging import voluptuous as vol -from homeassistant.components.discovery import SERVICE_AXIS +from homeassistant import config_entries from homeassistant.const import ( - ATTR_LOCATION, CONF_EVENT, CONF_HOST, CONF_INCLUDE, - CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME, CONF_USERNAME, + CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_TRIGGER_TIME, EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import config_validation as cv -from homeassistant.helpers import discovery -from homeassistant.helpers.dispatcher import dispatcher_send -from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['axis==16'] +from .config_flow import configured_devices, DEVICE_SCHEMA +from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN +from .device import AxisNetworkDevice, get_device -_LOGGER = logging.getLogger(__name__) - -DOMAIN = 'axis' -CONFIG_FILE = 'axis.conf' - -EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound', - 'daynight', 'tampering', 'input'] - -PLATFORMS = ['camera'] - -AXIS_INCLUDE = EVENT_TYPES + PLATFORMS - -AXIS_DEFAULT_HOST = '192.168.0.90' -AXIS_DEFAULT_USERNAME = 'root' -AXIS_DEFAULT_PASSWORD = 'pass' -DEFAULT_PORT = 80 - -DEVICE_SCHEMA = vol.Schema({ - vol.Required(CONF_INCLUDE): - vol.All(cv.ensure_list, [vol.In(AXIS_INCLUDE)]), - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_HOST, default=AXIS_DEFAULT_HOST): cv.string, - vol.Optional(CONF_USERNAME, default=AXIS_DEFAULT_USERNAME): cv.string, - vol.Optional(CONF_PASSWORD, default=AXIS_DEFAULT_PASSWORD): cv.string, - vol.Optional(CONF_TRIGGER_TIME, default=0): cv.positive_int, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(ATTR_LOCATION, default=''): cv.string, -}) +REQUIREMENTS = ['axis==17'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: cv.schema_with_slug_keys(DEVICE_SCHEMA), }, extra=vol.ALLOW_EXTRA) -SERVICE_VAPIX_CALL = 'vapix_call' -SERVICE_VAPIX_CALL_RESPONSE = 'vapix_call_response' -SERVICE_CGI = 'cgi' -SERVICE_ACTION = 'action' -SERVICE_PARAM = 'param' -SERVICE_DEFAULT_CGI = 'param.cgi' -SERVICE_DEFAULT_ACTION = 'update' - -SERVICE_SCHEMA = vol.Schema({ - vol.Required(CONF_NAME): cv.string, - vol.Required(SERVICE_PARAM): cv.string, - vol.Optional(SERVICE_CGI, default=SERVICE_DEFAULT_CGI): cv.string, - vol.Optional(SERVICE_ACTION, default=SERVICE_DEFAULT_ACTION): cv.string, -}) - - -def request_configuration(hass, config, name, host, serialnumber): - """Request configuration steps from the user.""" - configurator = hass.components.configurator - - def configuration_callback(callback_data): - """Call when configuration is submitted.""" - if CONF_INCLUDE not in callback_data: - configurator.notify_errors( - request_id, "Functionality mandatory.") - return False - - callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split() - callback_data[CONF_HOST] = host - - if CONF_NAME not in callback_data: - callback_data[CONF_NAME] = name - - try: - device_config = DEVICE_SCHEMA(callback_data) - except vol.Invalid: - configurator.notify_errors( - request_id, "Bad input, please check spelling.") - return False - - if setup_device(hass, config, device_config): - config_file = load_json(hass.config.path(CONFIG_FILE)) - config_file[serialnumber] = dict(device_config) - save_json(hass.config.path(CONFIG_FILE), config_file) - configurator.request_done(request_id) - else: - configurator.notify_errors( - request_id, "Failed to register, please try again.") - return False - title = '{} ({})'.format(name, host) - request_id = configurator.request_config( - title, configuration_callback, - description='Functionality: ' + str(AXIS_INCLUDE), - entity_picture="/static/images/logo_axis.png", - link_name='Axis platform documentation', - link_url='https://home-assistant.io/components/axis/', - submit_caption="Confirm", - fields=[ - {'id': CONF_NAME, - 'name': "Device name", - 'type': 'text'}, - {'id': CONF_USERNAME, - 'name': "User name", - 'type': 'text'}, - {'id': CONF_PASSWORD, - 'name': 'Password', - 'type': 'password'}, - {'id': CONF_INCLUDE, - 'name': "Device functionality (space separated list)", - 'type': 'text'}, - {'id': ATTR_LOCATION, - 'name': "Physical location of device (optional)", - 'type': 'text'}, - {'id': CONF_PORT, - 'name': "HTTP port (default=80)", - 'type': 'number'}, - {'id': CONF_TRIGGER_TIME, - 'name': "Sensor update interval (optional)", - 'type': 'number'}, - ] - ) - - -def setup(hass, config): +async def async_setup(hass, config): """Set up for Axis devices.""" - hass.data[DOMAIN] = {} + if DOMAIN in config: - def _shutdown(call): - """Stop the event stream on shutdown.""" - for serialnumber, device in hass.data[DOMAIN].items(): - _LOGGER.info("Stopping event stream for %s.", serialnumber) - device.stop() + for device_name, device_config in config[DOMAIN].items(): - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) + if CONF_NAME not in device_config: + device_config[CONF_NAME] = device_name - def axis_device_discovered(service, discovery_info): - """Call when axis devices has been found.""" - host = discovery_info[CONF_HOST] - name = discovery_info['hostname'] - serialnumber = discovery_info['properties']['macaddress'] + if device_config[CONF_HOST] not in configured_devices(hass): + hass.async_create_task(hass.config_entries.flow.async_init( + DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, + data=device_config + )) - if serialnumber not in hass.data[DOMAIN]: - config_file = load_json(hass.config.path(CONFIG_FILE)) - if serialnumber in config_file: - # Device config previously saved to file - try: - device_config = DEVICE_SCHEMA(config_file[serialnumber]) - device_config[CONF_HOST] = host - except vol.Invalid as err: - _LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err) - return False - if not setup_device(hass, config, device_config): - _LOGGER.error( - "Couldn't set up %s", device_config[CONF_NAME]) - else: - # New device, create configuration request for UI - request_configuration(hass, config, name, host, serialnumber) - else: - # Device already registered, but on a different IP - device = hass.data[DOMAIN][serialnumber] - device.config.host = host - dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host) + return True - # Register discovery service - discovery.listen(hass, SERVICE_AXIS, axis_device_discovered) - if DOMAIN in config: - for device in config[DOMAIN]: - device_config = config[DOMAIN][device] - if CONF_NAME not in device_config: - device_config[CONF_NAME] = device - if not setup_device(hass, config, device_config): - _LOGGER.error("Couldn't set up %s", device_config[CONF_NAME]) +async def async_setup_entry(hass, config_entry): + """Set up the Axis component.""" + if DOMAIN not in hass.data: + hass.data[DOMAIN] = {} - def vapix_service(call): - """Service to send a message.""" - for device in hass.data[DOMAIN].values(): - if device.name == call.data[CONF_NAME]: - response = device.vapix.do_request( - call.data[SERVICE_CGI], - call.data[SERVICE_ACTION], - call.data[SERVICE_PARAM]) - hass.bus.fire(SERVICE_VAPIX_CALL_RESPONSE, response) - return True - _LOGGER.info("Couldn't find device %s", call.data[CONF_NAME]) - return False + if not config_entry.options: + await async_populate_options(hass, config_entry) - # Register service with Home Assistant. - hass.services.register( - DOMAIN, SERVICE_VAPIX_CALL, vapix_service, schema=SERVICE_SCHEMA) - return True + device = AxisNetworkDevice(hass, config_entry) + if not await device.async_setup(): + return False -def setup_device(hass, config, device_config): - """Set up an Axis device.""" - import axis + hass.data[DOMAIN][device.serial] = device - def signal_callback(action, event): - """Call to configure events when initialized on event stream.""" - if action == 'add': - event_config = { - CONF_EVENT: event, - CONF_NAME: device_config[CONF_NAME], - ATTR_LOCATION: device_config[ATTR_LOCATION], - CONF_TRIGGER_TIME: device_config[CONF_TRIGGER_TIME] - } - component = event.event_platform - discovery.load_platform( - hass, component, DOMAIN, event_config, config) + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.shutdown) - event_types = [ - event - for event in device_config[CONF_INCLUDE] - if event in EVENT_TYPES - ] + return True - device = axis.AxisDevice( - loop=hass.loop, host=device_config[CONF_HOST], - username=device_config[CONF_USERNAME], - password=device_config[CONF_PASSWORD], - port=device_config[CONF_PORT], web_proto='http', - event_types=event_types, signal=signal_callback) - try: - hass.data[DOMAIN][device.vapix.serial_number] = device +async def async_populate_options(hass, config_entry): + """Populate default options for device.""" + from axis.vapix import VAPIX_IMAGE_FORMAT - except axis.Unauthorized: - _LOGGER.error("Credentials for %s are faulty", - device_config[CONF_HOST]) - return False + device = await get_device(hass, config_entry.data[CONF_DEVICE]) - except axis.RequestError: - return False + supported_formats = device.vapix.get_param(VAPIX_IMAGE_FORMAT) - device.name = device_config[CONF_NAME] + camera = bool(supported_formats) - for component in device_config[CONF_INCLUDE]: - if component == 'camera': - camera_config = { - CONF_NAME: device_config[CONF_NAME], - CONF_HOST: device_config[CONF_HOST], - CONF_PORT: device_config[CONF_PORT], - CONF_USERNAME: device_config[CONF_USERNAME], - CONF_PASSWORD: device_config[CONF_PASSWORD] - } - discovery.load_platform( - hass, component, DOMAIN, camera_config, config) + options = { + CONF_CAMERA: camera, + CONF_EVENTS: True, + CONF_TRIGGER_TIME: DEFAULT_TRIGGER_TIME + } - if event_types: - hass.add_job(device.start) - return True + hass.config_entries.async_update_entry(config_entry, options=options) diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index 11014dc4bc97cd..ec4c27ea34357b 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -1,86 +1,87 @@ """Support for Axis binary sensors.""" + from datetime import timedelta -import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.const import ( - ATTR_LOCATION, CONF_EVENT, CONF_NAME, CONF_TRIGGER_TIME) +from homeassistant.const import CONF_MAC, CONF_TRIGGER_TIME from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -DEPENDENCIES = ['axis'] +from .const import DOMAIN as AXIS_DOMAIN, LOGGER + +DEPENDENCIES = [AXIS_DOMAIN] + -_LOGGER = logging.getLogger(__name__) +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up a Axis binary sensor.""" + serial_number = config_entry.data[CONF_MAC] + device = hass.data[AXIS_DOMAIN][serial_number] + @callback + def async_add_sensor(event): + """Add binary sensor from Axis device.""" + async_add_entities([AxisBinarySensor(event, device)], True) -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the Axis binary devices.""" - add_entities([AxisBinarySensor(discovery_info)], True) + device.listeners.append( + async_dispatcher_connect(hass, 'axis_add_sensor', async_add_sensor)) class AxisBinarySensor(BinarySensorDevice): """Representation of a binary Axis event.""" - def __init__(self, event_config): + def __init__(self, event, device): """Initialize the Axis binary sensor.""" - self.axis_event = event_config[CONF_EVENT] - self.device_name = event_config[CONF_NAME] - self.location = event_config[ATTR_LOCATION] - self.delay = event_config[CONF_TRIGGER_TIME] + self.event = event + self.device = device + self.delay = device.config_entry.options[CONF_TRIGGER_TIME] self.remove_timer = None async def async_added_to_hass(self): """Subscribe sensors events.""" - self.axis_event.callback = self._update_callback + self.event.register_callback(self.update_callback) - def _update_callback(self): + def update_callback(self): """Update the sensor's state, if needed.""" + delay = self.device.config_entry.options[CONF_TRIGGER_TIME] + if self.remove_timer is not None: self.remove_timer() self.remove_timer = None - if self.delay == 0 or self.is_on: + if delay == 0 or self.is_on: self.schedule_update_ha_state() - else: # Run timer to delay updating the state - @callback - def _delay_update(now): - """Timer callback for sensor update.""" - _LOGGER.debug("%s called delayed (%s sec) update", - self.name, self.delay) - self.async_schedule_update_ha_state() - self.remove_timer = None - - self.remove_timer = async_track_point_in_utc_time( - self.hass, _delay_update, - utcnow() + timedelta(seconds=self.delay)) + return + + @callback + def _delay_update(now): + """Timer callback for sensor update.""" + LOGGER.debug("%s called delayed (%s sec) update", self.name, delay) + self.async_schedule_update_ha_state() + self.remove_timer = None + + self.remove_timer = async_track_point_in_utc_time( + self.hass, _delay_update, + utcnow() + timedelta(seconds=delay)) @property def is_on(self): """Return true if event is active.""" - return self.axis_event.is_tripped + return self.event.is_tripped @property def name(self): """Return the name of the event.""" - return '{}_{}_{}'.format( - self.device_name, self.axis_event.event_type, self.axis_event.id) + return '{} {} {}'.format( + self.device.name, self.event.event_type, self.event.id) @property def device_class(self): """Return the class of the event.""" - return self.axis_event.event_class + return self.event.event_class @property def should_poll(self): """No polling needed.""" return False - - @property - def device_state_attributes(self): - """Return the state attributes of the event.""" - attr = {} - - attr[ATTR_LOCATION] = self.location - - return attr diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index adf380eee4364b..60dab841048d2d 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -1,58 +1,59 @@ """Support for Axis camera streaming.""" -import logging from homeassistant.components.mjpeg.camera import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera, filter_urllib3_logging) from homeassistant.const import ( - CONF_AUTHENTICATION, CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT, - CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION) -from homeassistant.helpers.dispatcher import dispatcher_connect + CONF_AUTHENTICATION, CONF_DEVICE, CONF_HOST, CONF_MAC, CONF_NAME, + CONF_PASSWORD, CONF_PORT, CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION) +from homeassistant.helpers.dispatcher import async_dispatcher_connect -_LOGGER = logging.getLogger(__name__) +from .const import DOMAIN as AXIS_DOMAIN -DOMAIN = 'axis' -DEPENDENCIES = [DOMAIN] +DEPENDENCIES = [AXIS_DOMAIN] +AXIS_IMAGE = 'http://{}:{}/axis-cgi/jpg/image.cgi' +AXIS_VIDEO = 'http://{}:{}/axis-cgi/mjpg/video.cgi' -def _get_image_url(host, port, mode): - """Set the URL to get the image.""" - if mode == 'mjpeg': - return 'http://{}:{}/axis-cgi/mjpg/video.cgi'.format(host, port) - if mode == 'single': - return 'http://{}:{}/axis-cgi/jpg/image.cgi'.format(host, port) - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the Axis camera.""" +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the Axis camera video stream.""" filter_urllib3_logging() - camera_config = { - CONF_NAME: discovery_info[CONF_NAME], - CONF_USERNAME: discovery_info[CONF_USERNAME], - CONF_PASSWORD: discovery_info[CONF_PASSWORD], - CONF_MJPEG_URL: _get_image_url( - discovery_info[CONF_HOST], str(discovery_info[CONF_PORT]), - 'mjpeg'), - CONF_STILL_IMAGE_URL: _get_image_url( - discovery_info[CONF_HOST], str(discovery_info[CONF_PORT]), - 'single'), + serial_number = config_entry.data[CONF_MAC] + device = hass.data[AXIS_DOMAIN][serial_number] + + config = { + CONF_NAME: config_entry.data[CONF_NAME], + CONF_USERNAME: config_entry.data[CONF_DEVICE][CONF_USERNAME], + CONF_PASSWORD: config_entry.data[CONF_DEVICE][CONF_PASSWORD], + CONF_MJPEG_URL: AXIS_VIDEO.format( + config_entry.data[CONF_DEVICE][CONF_HOST], + config_entry.data[CONF_DEVICE][CONF_PORT]), + CONF_STILL_IMAGE_URL: AXIS_IMAGE.format( + config_entry.data[CONF_DEVICE][CONF_HOST], + config_entry.data[CONF_DEVICE][CONF_PORT]), CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION, } - add_entities([AxisCamera( - hass, camera_config, str(discovery_info[CONF_PORT]))]) + async_add_entities([AxisCamera(config, device)]) class AxisCamera(MjpegCamera): """Representation of a Axis camera.""" - def __init__(self, hass, config, port): + def __init__(self, config, device): """Initialize Axis Communications camera component.""" super().__init__(config) - self.port = port - dispatcher_connect( - hass, DOMAIN + '_' + config[CONF_NAME] + '_new_ip', self._new_ip) + self.device_config = config + self.device = device + self.port = device.config_entry.data[CONF_DEVICE][CONF_PORT] + self.unsub_dispatcher = None + + async def async_added_to_hass(self): + """Subscribe camera events.""" + self.unsub_dispatcher = async_dispatcher_connect( + self.hass, 'axis_{}_new_ip'.format(self.device.name), self._new_ip) def _new_ip(self, host): """Set new IP for video stream.""" - self._mjpeg_url = _get_image_url(host, self.port, 'mjpeg') - self._still_image_url = _get_image_url(host, self.port, 'single') + self._mjpeg_url = AXIS_VIDEO.format(host, self.port) + self._still_image_url = AXIS_IMAGE.format(host, self.port) diff --git a/homeassistant/components/axis/config_flow.py b/homeassistant/components/axis/config_flow.py new file mode 100644 index 00000000000000..24c286b140a659 --- /dev/null +++ b/homeassistant/components/axis/config_flow.py @@ -0,0 +1,202 @@ +"""Config flow to configure Axis devices.""" + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import ( + CONF_DEVICE, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_PORT, + CONF_USERNAME) +from homeassistant.core import callback +from homeassistant.helpers import config_validation as cv +from homeassistant.util.json import load_json + +from .const import CONF_MODEL, DOMAIN +from .device import get_device +from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect + +CONFIG_FILE = 'axis.conf' + +EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound', + 'daynight', 'tampering', 'input'] + +PLATFORMS = ['camera'] + +AXIS_INCLUDE = EVENT_TYPES + PLATFORMS + +AXIS_DEFAULT_HOST = '192.168.0.90' +AXIS_DEFAULT_USERNAME = 'root' +AXIS_DEFAULT_PASSWORD = 'pass' +DEFAULT_PORT = 80 + +DEVICE_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_HOST, default=AXIS_DEFAULT_HOST): cv.string, + vol.Optional(CONF_USERNAME, default=AXIS_DEFAULT_USERNAME): cv.string, + vol.Optional(CONF_PASSWORD, default=AXIS_DEFAULT_PASSWORD): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, +}, extra=vol.ALLOW_EXTRA) + + +@callback +def configured_devices(hass): + """Return a set of the configured devices.""" + return set(entry.data[CONF_DEVICE][CONF_HOST] for entry + in hass.config_entries.async_entries(DOMAIN)) + + +@config_entries.HANDLERS.register(DOMAIN) +class AxisFlowHandler(config_entries.ConfigFlow): + """Handle a Axis config flow.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + + def __init__(self): + """Initialize the Axis config flow.""" + self.device_config = {} + self.model = None + self.name = None + self.serial_number = None + + self.discovery_schema = {} + self.import_schema = {} + + async def async_step_user(self, user_input=None): + """Handle a Axis config flow start. + + Manage device specific parameters. + """ + from axis.vapix import VAPIX_MODEL_ID, VAPIX_SERIAL_NUMBER + errors = {} + + if user_input is not None: + try: + if user_input[CONF_HOST] in configured_devices(self.hass): + raise AlreadyConfigured + + self.device_config = { + CONF_HOST: user_input[CONF_HOST], + CONF_PORT: user_input[CONF_PORT], + CONF_USERNAME: user_input[CONF_USERNAME], + CONF_PASSWORD: user_input[CONF_PASSWORD] + } + device = await get_device(self.hass, self.device_config) + + self.serial_number = device.vapix.get_param( + VAPIX_SERIAL_NUMBER) + self.model = device.vapix.get_param(VAPIX_MODEL_ID) + + return await self._create_entry() + + except AlreadyConfigured: + errors['base'] = 'already_configured' + + except AuthenticationRequired: + errors['base'] = 'faulty_credentials' + + except CannotConnect: + errors['base'] = 'device_unavailable' + + data = self.import_schema or self.discovery_schema or { + vol.Required(CONF_HOST): str, + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_PORT, default=DEFAULT_PORT): int + } + + return self.async_show_form( + step_id='user', + description_placeholders=self.device_config, + data_schema=vol.Schema(data), + errors=errors + ) + + async def _create_entry(self): + """Create entry for device. + + Generate a name to be used as a prefix for device entities. + """ + if self.name is None: + same_model = [ + entry.data[CONF_NAME] for entry + in self.hass.config_entries.async_entries(DOMAIN) + if entry.data[CONF_MODEL] == self.model + ] + + self.name = "{}".format(self.model) + for idx in range(len(same_model) + 1): + self.name = "{} {}".format(self.model, idx) + if self.name not in same_model: + break + + data = { + CONF_DEVICE: self.device_config, + CONF_NAME: self.name, + CONF_MAC: self.serial_number, + CONF_MODEL: self.model, + } + + title = "{} - {}".format(self.model, self.serial_number) + return self.async_create_entry( + title=title, + data=data + ) + + async def async_step_discovery(self, discovery_info): + """Prepare configuration for a discovered Axis device. + + This flow is triggered by the discovery component. + """ + if discovery_info[CONF_HOST] in configured_devices(self.hass): + return self.async_abort(reason='already_configured') + + if discovery_info[CONF_HOST].startswith('169.254'): + return self.async_abort(reason='link_local_address') + + config_file = await self.hass.async_add_executor_job( + load_json, self.hass.config.path(CONFIG_FILE)) + + serialnumber = discovery_info['properties']['macaddress'] + + if serialnumber not in config_file: + self.discovery_schema = { + vol.Required( + CONF_HOST, default=discovery_info[CONF_HOST]): str, + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_PORT, default=discovery_info[CONF_PORT]): int + } + return await self.async_step_user() + + try: + device_config = DEVICE_SCHEMA(config_file[serialnumber]) + device_config[CONF_HOST] = discovery_info[CONF_HOST] + + if CONF_NAME not in device_config: + device_config[CONF_NAME] = discovery_info['hostname'] + + except vol.Invalid: + return self.async_abort(reason='bad_config_file') + + return await self.async_step_import(device_config) + + async def async_step_import(self, import_config): + """Import a Axis device as a config entry. + + This flow is triggered by `async_setup` for configured devices. + This flow is also triggered by `async_step_discovery`. + + This will execute for any Axis device that contains a complete + configuration. + """ + self.name = import_config[CONF_NAME] + + self.import_schema = { + vol.Required(CONF_HOST, default=import_config[CONF_HOST]): str, + vol.Required( + CONF_USERNAME, default=import_config[CONF_USERNAME]): str, + vol.Required( + CONF_PASSWORD, default=import_config[CONF_PASSWORD]): str, + vol.Required(CONF_PORT, default=import_config[CONF_PORT]): int + } + return await self.async_step_user(user_input=import_config) diff --git a/homeassistant/components/axis/const.py b/homeassistant/components/axis/const.py new file mode 100644 index 00000000000000..c6cd697612983e --- /dev/null +++ b/homeassistant/components/axis/const.py @@ -0,0 +1,12 @@ +"""Constants for the Axis component.""" +import logging + +LOGGER = logging.getLogger('homeassistant.components.axis') + +DOMAIN = 'axis' + +CONF_CAMERA = 'camera' +CONF_EVENTS = 'events' +CONF_MODEL = 'model' + +DEFAULT_TRIGGER_TIME = 0 diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py new file mode 100644 index 00000000000000..02591e348a5dbf --- /dev/null +++ b/homeassistant/components/axis/device.py @@ -0,0 +1,127 @@ +"""Axis network device abstraction.""" + +import asyncio +import async_timeout + +from homeassistant.const import ( + CONF_DEVICE, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_PORT, + CONF_USERNAME) +from homeassistant.core import callback +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.dispatcher import async_dispatcher_send + +from .const import CONF_CAMERA, CONF_EVENTS, CONF_MODEL, LOGGER +from .errors import AuthenticationRequired, CannotConnect + + +class AxisNetworkDevice: + """Manages a Axis device.""" + + def __init__(self, hass, config_entry): + """Initialize the device.""" + self.hass = hass + self.config_entry = config_entry + self.available = True + + self.api = None + self.fw_version = None + self.product_type = None + + self.listeners = [] + + @property + def host(self): + """Return the host of this device.""" + return self.config_entry.data[CONF_DEVICE][CONF_HOST] + + @property + def model(self): + """Return the model of this device.""" + return self.config_entry.data[CONF_MODEL] + + @property + def name(self): + """Return the name of this device.""" + return self.config_entry.data[CONF_NAME] + + @property + def serial(self): + """Return the mac of this device.""" + return self.config_entry.data[CONF_MAC] + + async def async_setup(self): + """Set up the device.""" + from axis.vapix import VAPIX_FW_VERSION, VAPIX_PROD_TYPE + + hass = self.hass + + try: + self.api = await get_device( + hass, self.config_entry.data[CONF_DEVICE], + event_types='on', signal_callback=self.async_signal_callback) + + except CannotConnect: + raise ConfigEntryNotReady + + except Exception: # pylint: disable=broad-except + LOGGER.error( + 'Unknown error connecting with Axis device on %s', self.host) + return False + + self.fw_version = self.api.vapix.get_param(VAPIX_FW_VERSION) + self.product_type = self.api.vapix.get_param(VAPIX_PROD_TYPE) + + if self.config_entry.options[CONF_CAMERA]: + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, 'camera')) + + if self.config_entry.options[CONF_EVENTS]: + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, 'binary_sensor')) + self.api.start() + + return True + + @callback + def async_signal_callback(self, action, event): + """Call to configure events when initialized on event stream.""" + if action == 'add': + async_dispatcher_send(self.hass, 'axis_add_sensor', event) + + @callback + def shutdown(self, event): + """Stop the event stream.""" + self.api.stop() + + +async def get_device(hass, config, event_types=None, signal_callback=None): + """Create a Axis device.""" + import axis + + device = axis.AxisDevice( + loop=hass.loop, host=config[CONF_HOST], + username=config[CONF_USERNAME], + password=config[CONF_PASSWORD], + port=config[CONF_PORT], web_proto='http', + event_types=event_types, signal=signal_callback) + + try: + with async_timeout.timeout(15): + await hass.async_add_executor_job(device.vapix.load_params) + return device + + except axis.Unauthorized: + LOGGER.warning("Connected to device at %s but not registered.", + config[CONF_HOST]) + raise AuthenticationRequired + + except (asyncio.TimeoutError, axis.RequestError): + LOGGER.error("Error connecting to the Axis device at %s", + config[CONF_HOST]) + raise CannotConnect + + except axis.AxisException: + LOGGER.exception('Unknown Axis communication error occurred') + raise AuthenticationRequired diff --git a/homeassistant/components/axis/errors.py b/homeassistant/components/axis/errors.py new file mode 100644 index 00000000000000..56105b28b1bfda --- /dev/null +++ b/homeassistant/components/axis/errors.py @@ -0,0 +1,22 @@ +"""Errors for the Axis component.""" +from homeassistant.exceptions import HomeAssistantError + + +class AxisException(HomeAssistantError): + """Base class for Axis exceptions.""" + + +class AlreadyConfigured(AxisException): + """Device is already configured.""" + + +class AuthenticationRequired(AxisException): + """Unknown error occurred.""" + + +class CannotConnect(AxisException): + """Unable to connect to the device.""" + + +class UserLevel(AxisException): + """User level too low.""" diff --git a/homeassistant/components/axis/services.yaml b/homeassistant/components/axis/services.yaml deleted file mode 100644 index 03db5ce7af8a43..00000000000000 --- a/homeassistant/components/axis/services.yaml +++ /dev/null @@ -1,15 +0,0 @@ -vapix_call: - description: Configure device using Vapix parameter management. - fields: - name: - description: Name of device to Configure. [Required] - example: M1065-W - cgi: - description: Which cgi to call on device. [Optional] Default is 'param.cgi' - example: 'applications/control.cgi' - action: - description: What type of call. [Optional] Default is 'update' - example: 'start' - param: - description: What parameter to operate on. [Required] - example: 'package=VideoMotionDetection' \ No newline at end of file diff --git a/homeassistant/components/axis/strings.json b/homeassistant/components/axis/strings.json new file mode 100644 index 00000000000000..3c528dfbb16112 --- /dev/null +++ b/homeassistant/components/axis/strings.json @@ -0,0 +1,26 @@ +{ + "config": { + "title": "Axis device", + "step": { + "user": { + "title": "Set up Axis device", + "data": { + "host": "Host", + "username": "Username", + "password": "Password", + "port": "Port" + } + } + }, + "error": { + "already_configured": "Device is already configured", + "device_unavailable": "Device is not available", + "faulty_credentials": "Bad user credentials" + }, + "abort": { + "already_configured": "Device is already configured", + "bad_config_file": "Bad data from config file", + "link_local_address": "Link local addresses are not supported" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 1fb727642bc50d..ecbbe7ea5e0657 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -52,6 +52,7 @@ SERVICE_XIAOMI_GW = 'xiaomi_gw' CONFIG_ENTRY_HANDLERS = { + SERVICE_AXIS: 'axis', SERVICE_DAIKIN: 'daikin', SERVICE_DECONZ: 'deconz', 'esphome': 'esphome', @@ -69,7 +70,6 @@ SERVICE_NETGEAR: ('device_tracker', None), SERVICE_WEMO: ('wemo', None), SERVICE_HASSIO: ('hassio', None), - SERVICE_AXIS: ('axis', None), SERVICE_APPLE_TV: ('apple_tv', None), SERVICE_ENIGMA2: ('media_player', 'enigma2'), SERVICE_ROKU: ('roku', None), diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index e00d7204a793bc..df635807abe39c 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -143,6 +143,7 @@ async def async_step_discovery(info): # Components that have config flows. In future we will auto-generate this list. FLOWS = [ 'ambient_station', + 'axis', 'cast', 'daikin', 'deconz', diff --git a/requirements_all.txt b/requirements_all.txt index ea91ef5e9f40c8..14e845074e6981 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -186,7 +186,7 @@ av==6.1.2 # avion==0.10 # homeassistant.components.axis -axis==16 +axis==17 # homeassistant.components.tts.baidu baidu-aip==1.6.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b532b7b386d530..731f7fa9d22115 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -56,6 +56,9 @@ apns2==0.3.0 # homeassistant.components.stream av==6.1.2 +# homeassistant.components.axis +axis==17 + # homeassistant.components.zha bellows-homeassistant==0.7.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index fa6a8429ff3218..3c605ef7ae33b6 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -46,6 +46,7 @@ 'aiounifi', 'apns2', 'av', + 'axis', 'caldav', 'coinmarketcap', 'defusedxml', diff --git a/tests/components/axis/__init__.py b/tests/components/axis/__init__.py new file mode 100644 index 00000000000000..c7e0f05a81477f --- /dev/null +++ b/tests/components/axis/__init__.py @@ -0,0 +1 @@ +"""Tests for the Axis component.""" diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py new file mode 100644 index 00000000000000..9ca8b81793ba6a --- /dev/null +++ b/tests/components/axis/test_binary_sensor.py @@ -0,0 +1,102 @@ +"""Axis binary sensor platform tests.""" + +from unittest.mock import Mock + +from homeassistant import config_entries +from homeassistant.components import axis +from homeassistant.setup import async_setup_component + +import homeassistant.components.binary_sensor as binary_sensor + +EVENTS = [ + { + 'operation': 'Initialized', + 'topic': 'tns1:Device/tnsaxis:Sensor/PIR', + 'source': 'sensor', + 'source_idx': '0', + 'type': 'state', + 'value': '0' + }, + { + 'operation': 'Initialized', + 'topic': 'tnsaxis:CameraApplicationPlatform/VMD/Camera1Profile1', + 'type': 'active', + 'value': '1' + } +] + +ENTRY_CONFIG = { + axis.CONF_DEVICE: { + axis.config_flow.CONF_HOST: '1.2.3.4', + axis.config_flow.CONF_USERNAME: 'user', + axis.config_flow.CONF_PASSWORD: 'pass', + axis.config_flow.CONF_PORT: 80 + }, + axis.config_flow.CONF_MAC: '1234ABCD', + axis.config_flow.CONF_MODEL: 'model', + axis.config_flow.CONF_NAME: 'model 0' +} + +ENTRY_OPTIONS = { + axis.CONF_CAMERA: False, + axis.CONF_EVENTS: True, + axis.CONF_TRIGGER_TIME: 0 +} + + +async def setup_device(hass): + """Load the Axis binary sensor platform.""" + from axis import AxisDevice + loop = Mock() + + config_entry = config_entries.ConfigEntry( + 1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test', + config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS) + device = axis.AxisNetworkDevice(hass, config_entry) + device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE], + signal=device.async_signal_callback) + hass.data[axis.DOMAIN] = {device.serial: device} + + await hass.config_entries.async_forward_entry_setup( + config_entry, 'binary_sensor') + # To flush out the service call to update the group + await hass.async_block_till_done() + + return device + + +async def test_platform_manually_configured(hass): + """Test that nothing happens when platform is manually configured.""" + assert await async_setup_component(hass, binary_sensor.DOMAIN, { + 'binary_sensor': { + 'platform': axis.DOMAIN + } + }) is True + + assert axis.DOMAIN not in hass.data + + +async def test_no_binary_sensors(hass): + """Test that no sensors in Axis results in no sensor entities.""" + await setup_device(hass) + + assert len(hass.states.async_all()) == 0 + + +async def test_binary_sensors(hass): + """Test that sensors are loaded properly.""" + device = await setup_device(hass) + + for event in EVENTS: + device.api.stream.event.manage_event(event) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 2 + + pir = hass.states.get('binary_sensor.model_0_pir_0') + assert pir.state == 'off' + assert pir.name == 'model 0 PIR 0' + + vmd4 = hass.states.get('binary_sensor.model_0_vmd4_camera1profile1') + assert vmd4.state == 'on' + assert vmd4.name == 'model 0 VMD4 Camera1Profile1' diff --git a/tests/components/axis/test_camera.py b/tests/components/axis/test_camera.py new file mode 100644 index 00000000000000..c585ada631978f --- /dev/null +++ b/tests/components/axis/test_camera.py @@ -0,0 +1,73 @@ +"""Axis camera platform tests.""" + +from unittest.mock import Mock + +from homeassistant import config_entries +from homeassistant.components import axis +from homeassistant.setup import async_setup_component + +import homeassistant.components.camera as camera + + +ENTRY_CONFIG = { + axis.CONF_DEVICE: { + axis.config_flow.CONF_HOST: '1.2.3.4', + axis.config_flow.CONF_USERNAME: 'user', + axis.config_flow.CONF_PASSWORD: 'pass', + axis.config_flow.CONF_PORT: 80 + }, + axis.config_flow.CONF_MAC: '1234ABCD', + axis.config_flow.CONF_MODEL: 'model', + axis.config_flow.CONF_NAME: 'model 0' +} + +ENTRY_OPTIONS = { + axis.CONF_CAMERA: False, + axis.CONF_EVENTS: True, + axis.CONF_TRIGGER_TIME: 0 +} + + +async def setup_device(hass): + """Load the Axis binary sensor platform.""" + from axis import AxisDevice + loop = Mock() + + config_entry = config_entries.ConfigEntry( + 1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test', + config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS) + device = axis.AxisNetworkDevice(hass, config_entry) + device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE], + signal=device.async_signal_callback) + hass.data[axis.DOMAIN] = {device.serial: device} + + await hass.config_entries.async_forward_entry_setup( + config_entry, 'camera') + # To flush out the service call to update the group + await hass.async_block_till_done() + + return device + + +async def test_platform_manually_configured(hass): + """Test that nothing happens when platform is manually configured.""" + assert await async_setup_component(hass, camera.DOMAIN, { + 'camera': { + 'platform': axis.DOMAIN + } + }) is True + + assert axis.DOMAIN not in hass.data + + +async def test_camera(hass): + """Test that Axis camera platform is loaded properly.""" + await setup_device(hass) + + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 1 + + cam = hass.states.get('camera.model_0') + assert cam.state == 'idle' + assert cam.name == 'model 0' diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py new file mode 100644 index 00000000000000..7e18b36c6a6c27 --- /dev/null +++ b/tests/components/axis/test_config_flow.py @@ -0,0 +1,319 @@ +"""Test Axis config flow.""" +from unittest.mock import Mock, patch + +from homeassistant.components import axis +from homeassistant.components.axis import config_flow + +from tests.common import mock_coro, MockConfigEntry + +import axis as axis_lib + + +async def test_configured_devices(hass): + """Test that configured devices works as expected.""" + result = config_flow.configured_devices(hass) + + assert not result + + entry = MockConfigEntry(domain=axis.DOMAIN, + data={axis.CONF_DEVICE: {axis.CONF_HOST: ''}}) + entry.add_to_hass(hass) + + result = config_flow.configured_devices(hass) + + assert len(result) == 1 + + +async def test_flow_works(hass): + """Test that config flow works.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('axis.AxisDevice') as mock_device: + def mock_constructor( + loop, host, username, password, port, web_proto, event_types, + signal): + """Fake the controller constructor.""" + mock_device.loop = loop + mock_device.host = host + mock_device.username = username + mock_device.password = password + mock_device.port = port + return mock_device + + def mock_get_param(param): + """Fake get param method.""" + return param + + mock_device.side_effect = mock_constructor + mock_device.vapix.load_params.return_value = Mock() + mock_device.vapix.get_param.side_effect = mock_get_param + + result = await flow.async_step_user(user_input={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) + + assert result['type'] == 'create_entry' + assert result['title'] == '{} - {}'.format( + axis_lib.vapix.VAPIX_MODEL_ID, axis_lib.vapix.VAPIX_SERIAL_NUMBER) + assert result['data'] == { + axis.CONF_DEVICE: { + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }, + config_flow.CONF_MAC: axis_lib.vapix.VAPIX_SERIAL_NUMBER, + config_flow.CONF_MODEL: axis_lib.vapix.VAPIX_MODEL_ID, + config_flow.CONF_NAME: 'Brand.ProdNbr 0' + } + + +async def test_flow_fails_already_configured(hass): + """Test that config flow fails on already configured device.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + entry = MockConfigEntry(domain=axis.DOMAIN, data={axis.CONF_DEVICE: { + axis.CONF_HOST: '1.2.3.4' + }}) + entry.add_to_hass(hass) + + result = await flow.async_step_user(user_input={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) + + assert result['errors'] == {'base': 'already_configured'} + + +async def test_flow_fails_faulty_credentials(hass): + """Test that config flow fails on faulty credentials.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('homeassistant.components.axis.config_flow.get_device', + side_effect=config_flow.AuthenticationRequired): + result = await flow.async_step_user(user_input={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) + + assert result['errors'] == {'base': 'faulty_credentials'} + + +async def test_flow_fails_device_unavailable(hass): + """Test that config flow fails on device unavailable.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('homeassistant.components.axis.config_flow.get_device', + side_effect=config_flow.CannotConnect): + result = await flow.async_step_user(user_input={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) + + assert result['errors'] == {'base': 'device_unavailable'} + + +async def test_flow_create_entry(hass): + """Test that create entry can generate a name without other entries.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + flow.model = 'model' + + result = await flow._create_entry() + + assert result['data'][config_flow.CONF_NAME] == 'model 0' + + +async def test_flow_create_entry_more_entries(hass): + """Test that create entry can generate a name with other entries.""" + entry = MockConfigEntry( + domain=axis.DOMAIN, data={config_flow.CONF_NAME: 'model 0', + config_flow.CONF_MODEL: 'model'}) + entry.add_to_hass(hass) + entry2 = MockConfigEntry( + domain=axis.DOMAIN, data={config_flow.CONF_NAME: 'model 1', + config_flow.CONF_MODEL: 'model'}) + entry2.add_to_hass(hass) + + flow = config_flow.AxisFlowHandler() + flow.hass = hass + flow.model = 'model' + + result = await flow._create_entry() + + assert result['data'][config_flow.CONF_NAME] == 'model 2' + + +async def test_discovery_flow(hass): + """Test that discovery for new devices work.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch.object(axis, 'get_device', return_value=mock_coro(Mock())): + result = await flow.async_step_discovery(discovery_info={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_PORT: 80, + 'properties': {'macaddress': '1234'} + }) + + assert result['type'] == 'form' + assert result['step_id'] == 'user' + + +async def test_discovery_flow_known_device(hass): + """Test that discovery for known devices work. + + This is legacy support from devices registered with configurator. + """ + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('homeassistant.components.axis.config_flow.load_json', + return_value={'1234ABCD': { + config_flow.CONF_HOST: '2.3.4.5', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 80}}), \ + patch('axis.AxisDevice') as mock_device: + def mock_constructor( + loop, host, username, password, port, web_proto, event_types, + signal): + """Fake the controller constructor.""" + mock_device.loop = loop + mock_device.host = host + mock_device.username = username + mock_device.password = password + mock_device.port = port + return mock_device + + def mock_get_param(param): + """Fake get param method.""" + return param + + mock_device.side_effect = mock_constructor + mock_device.vapix.load_params.return_value = Mock() + mock_device.vapix.get_param.side_effect = mock_get_param + + result = await flow.async_step_discovery(discovery_info={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_PORT: 80, + 'hostname': 'name', + 'properties': {'macaddress': '1234ABCD'} + }) + + assert result['type'] == 'create_entry' + + +async def test_discovery_flow_already_configured(hass): + """Test that discovery doesn't setup already configured devices.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + entry = MockConfigEntry(domain=axis.DOMAIN, data={axis.CONF_DEVICE: { + axis.CONF_HOST: '1.2.3.4' + }}) + entry.add_to_hass(hass) + + result = await flow.async_step_discovery(discovery_info={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) + print(result) + assert result['type'] == 'abort' + + +async def test_discovery_flow_link_local_address(hass): + """Test that discovery doesn't setup devices with link local addresses.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + result = await flow.async_step_discovery(discovery_info={ + config_flow.CONF_HOST: '169.254.3.4' + }) + + assert result['type'] == 'abort' + + +async def test_discovery_flow_bad_config_file(hass): + """Test that discovery with bad config files abort.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('homeassistant.components.axis.config_flow.load_json', + return_value={'1234ABCD': { + config_flow.CONF_HOST: '2.3.4.5', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 80}}), \ + patch('homeassistant.components.axis.config_flow.DEVICE_SCHEMA', + side_effect=config_flow.vol.Invalid('')): + result = await flow.async_step_discovery(discovery_info={ + config_flow.CONF_HOST: '1.2.3.4', + 'properties': {'macaddress': '1234ABCD'} + }) + + assert result['type'] == 'abort' + + +async def test_import_flow_works(hass): + """Test that import flow works.""" + flow = config_flow.AxisFlowHandler() + flow.hass = hass + + with patch('axis.AxisDevice') as mock_device: + def mock_constructor( + loop, host, username, password, port, web_proto, event_types, + signal): + """Fake the controller constructor.""" + mock_device.loop = loop + mock_device.host = host + mock_device.username = username + mock_device.password = password + mock_device.port = port + return mock_device + + def mock_get_param(param): + """Fake get param method.""" + return param + + mock_device.side_effect = mock_constructor + mock_device.vapix.load_params.return_value = Mock() + mock_device.vapix.get_param.side_effect = mock_get_param + + result = await flow.async_step_import(import_config={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81, + config_flow.CONF_NAME: 'name' + }) + + assert result['type'] == 'create_entry' + assert result['title'] == '{} - {}'.format( + axis_lib.vapix.VAPIX_MODEL_ID, axis_lib.vapix.VAPIX_SERIAL_NUMBER) + assert result['data'] == { + axis.CONF_DEVICE: { + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }, + config_flow.CONF_MAC: axis_lib.vapix.VAPIX_SERIAL_NUMBER, + config_flow.CONF_MODEL: axis_lib.vapix.VAPIX_MODEL_ID, + config_flow.CONF_NAME: 'name' + } diff --git a/tests/components/axis/test_device.py b/tests/components/axis/test_device.py new file mode 100644 index 00000000000000..2a0a7d6391cb02 --- /dev/null +++ b/tests/components/axis/test_device.py @@ -0,0 +1,152 @@ +"""Test Axis device.""" +from unittest.mock import Mock, patch + +import pytest + +from tests.common import mock_coro + +from homeassistant.components.axis import device, errors + +DEVICE_DATA = { + device.CONF_HOST: '1.2.3.4', + device.CONF_USERNAME: 'username', + device.CONF_PASSWORD: 'password', + device.CONF_PORT: 1234 +} + +ENTRY_OPTIONS = { + device.CONF_CAMERA: True, + device.CONF_EVENTS: ['pir'], +} + +ENTRY_CONFIG = { + device.CONF_DEVICE: DEVICE_DATA, + device.CONF_MAC: 'mac', + device.CONF_MODEL: 'model', + device.CONF_NAME: 'name' +} + + +async def test_device_setup(): + """Successful setup.""" + hass = Mock() + entry = Mock() + entry.data = ENTRY_CONFIG + entry.options = ENTRY_OPTIONS + api = Mock() + + axis_device = device.AxisNetworkDevice(hass, entry) + + assert axis_device.host == DEVICE_DATA[device.CONF_HOST] + assert axis_device.model == ENTRY_CONFIG[device.CONF_MODEL] + assert axis_device.name == ENTRY_CONFIG[device.CONF_NAME] + assert axis_device.serial == ENTRY_CONFIG[device.CONF_MAC] + + with patch.object(device, 'get_device', return_value=mock_coro(api)): + assert await axis_device.async_setup() is True + + assert axis_device.api is api + assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 2 + assert hass.config_entries.async_forward_entry_setup.mock_calls[0][1] == \ + (entry, 'camera') + assert hass.config_entries.async_forward_entry_setup.mock_calls[1][1] == \ + (entry, 'binary_sensor') + + +async def test_device_not_accessible(): + """Failed setup schedules a retry of setup.""" + hass = Mock() + hass.data = dict() + entry = Mock() + entry.data = ENTRY_CONFIG + entry.options = ENTRY_OPTIONS + + axis_device = device.AxisNetworkDevice(hass, entry) + + with patch.object(device, 'get_device', + side_effect=errors.CannotConnect), \ + pytest.raises(device.ConfigEntryNotReady): + await axis_device.async_setup() + + assert not hass.helpers.event.async_call_later.mock_calls + + +async def test_device_unknown_error(): + """Unknown errors are handled.""" + hass = Mock() + entry = Mock() + entry.data = ENTRY_CONFIG + entry.options = ENTRY_OPTIONS + + axis_device = device.AxisNetworkDevice(hass, entry) + + with patch.object(device, 'get_device', side_effect=Exception): + assert await axis_device.async_setup() is False + + assert not hass.helpers.event.async_call_later.mock_calls + + +async def test_new_event_sends_signal(hass): + """Make sure that new event send signal.""" + entry = Mock() + entry.data = ENTRY_CONFIG + + axis_device = device.AxisNetworkDevice(hass, entry) + + with patch.object(device, 'async_dispatcher_send') as mock_dispatch_send: + axis_device.async_signal_callback(action='add', event='event') + await hass.async_block_till_done() + + assert len(mock_dispatch_send.mock_calls) == 1 + assert len(mock_dispatch_send.mock_calls[0]) == 3 + + +async def test_shutdown(): + """Successful shutdown.""" + hass = Mock() + entry = Mock() + entry.data = ENTRY_CONFIG + + axis_device = device.AxisNetworkDevice(hass, entry) + axis_device.api = Mock() + + axis_device.shutdown(None) + + assert len(axis_device.api.stop.mock_calls) == 1 + + +async def test_get_device(hass): + """Successful call.""" + with patch('axis.vapix.Vapix.load_params', + return_value=mock_coro()): + assert await device.get_device(hass, DEVICE_DATA) + + +async def test_get_device_fails(hass): + """Device unauthorized yields authentication required error.""" + import axis + + with patch('axis.vapix.Vapix.load_params', + side_effect=axis.Unauthorized), \ + pytest.raises(errors.AuthenticationRequired): + await device.get_device(hass, DEVICE_DATA) + + +async def test_get_device_device_unavailable(hass): + """Device unavailable yields cannot connect error.""" + import axis + + with patch('axis.vapix.Vapix.load_params', + side_effect=axis.RequestError), \ + pytest.raises(errors.CannotConnect): + await device.get_device(hass, DEVICE_DATA) + + +async def test_get_device_unknown_error(hass): + """Device yield unknown error.""" + import axis + + with patch('axis.vapix.Vapix.load_params', + side_effect=axis.AxisException), \ + pytest.raises(errors.AuthenticationRequired): + await device.get_device(hass, DEVICE_DATA) diff --git a/tests/components/axis/test_init.py b/tests/components/axis/test_init.py new file mode 100644 index 00000000000000..0586ffd96f6640 --- /dev/null +++ b/tests/components/axis/test_init.py @@ -0,0 +1,97 @@ +"""Test Axis component setup process.""" +from unittest.mock import Mock, patch + +from homeassistant.setup import async_setup_component +from homeassistant.components import axis + +from tests.common import mock_coro, MockConfigEntry + + +async def test_setup(hass): + """Test configured options for a device are loaded via config entry.""" + with patch.object(hass, 'config_entries') as mock_config_entries, \ + patch.object(axis, 'configured_devices', return_value={}): + + assert await async_setup_component(hass, axis.DOMAIN, { + axis.DOMAIN: { + 'device_name': { + axis.CONF_HOST: '1.2.3.4', + axis.config_flow.CONF_PORT: 80, + } + } + }) + + assert len(mock_config_entries.flow.mock_calls) == 1 + + +async def test_setup_device_already_configured(hass): + """Test already configured device does not configure a second.""" + with patch.object(hass, 'config_entries') as mock_config_entries, \ + patch.object(axis, 'configured_devices', return_value={'1.2.3.4'}): + + assert await async_setup_component(hass, axis.DOMAIN, { + axis.DOMAIN: { + 'device_name': { + axis.CONF_HOST: '1.2.3.4' + } + } + }) + + assert not mock_config_entries.flow.mock_calls + + +async def test_setup_no_config(hass): + """Test setup without configuration.""" + assert await async_setup_component(hass, axis.DOMAIN, {}) + assert axis.DOMAIN not in hass.data + + +async def test_setup_entry(hass): + """Test successful setup of entry.""" + entry = MockConfigEntry( + domain=axis.DOMAIN, data={axis.device.CONF_MAC: '0123'}) + + mock_device = Mock() + mock_device.async_setup.return_value = mock_coro(True) + mock_device.serial.return_value = '1' + + with patch.object(axis, 'AxisNetworkDevice') as mock_device_class, \ + patch.object( + axis, 'async_populate_options', return_value=mock_coro(True)): + mock_device_class.return_value = mock_device + + assert await axis.async_setup_entry(hass, entry) + + assert len(hass.data[axis.DOMAIN]) == 1 + + +async def test_setup_entry_fails(hass): + """Test successful setup of entry.""" + entry = MockConfigEntry( + domain=axis.DOMAIN, data={axis.device.CONF_MAC: '0123'}, options=True) + + mock_device = Mock() + mock_device.async_setup.return_value = mock_coro(False) + + with patch.object(axis, 'AxisNetworkDevice') as mock_device_class: + mock_device_class.return_value = mock_device + + assert not await axis.async_setup_entry(hass, entry) + + assert not hass.data[axis.DOMAIN] + + +async def test_populate_options(hass): + """Test successful populate options.""" + entry = MockConfigEntry(domain=axis.DOMAIN, data={'device': {}}) + entry.add_to_hass(hass) + + with patch.object(axis, 'get_device', return_value=mock_coro(Mock())): + + await axis.async_populate_options(hass, entry) + + assert entry.options == { + axis.CONF_CAMERA: True, + axis.CONF_EVENTS: True, + axis.CONF_TRIGGER_TIME: axis.DEFAULT_TRIGGER_TIME + } From ed93c3b2c172012586ac3521fb50336f704c1ef9 Mon Sep 17 00:00:00 2001 From: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com> Date: Sun, 24 Mar 2019 13:37:31 -0400 Subject: [PATCH 115/605] Fix pressure in dark sky and openweathermap and add pressure utility (#21210) --- homeassistant/components/darksky/weather.py | 10 ++- .../components/openweathermap/weather.py | 10 ++- homeassistant/const.py | 8 +++ homeassistant/util/pressure.py | 51 ++++++++++++++ homeassistant/util/unit_system.py | 52 ++++++++------- tests/helpers/test_template.py | 3 +- tests/util/test_pressure.py | 66 +++++++++++++++++++ tests/util/test_unit_system.py | 48 ++++++++++++-- 8 files changed, 212 insertions(+), 36 deletions(-) create mode 100644 homeassistant/util/pressure.py create mode 100644 tests/util/test_pressure.py diff --git a/homeassistant/components/darksky/weather.py b/homeassistant/components/darksky/weather.py index d5cbcb4785ae96..5b3db4312bfb10 100644 --- a/homeassistant/components/darksky/weather.py +++ b/homeassistant/components/darksky/weather.py @@ -12,10 +12,10 @@ ATTR_FORECAST_WIND_SPEED, PLATFORM_SCHEMA, WeatherEntity) from homeassistant.const import ( CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE, CONF_NAME, - TEMP_CELSIUS, TEMP_FAHRENHEIT) + PRESSURE_HPA, PRESSURE_INHG, TEMP_CELSIUS, TEMP_FAHRENHEIT) import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle - +from homeassistant.util.pressure import convert as convert_pressure REQUIREMENTS = ['python-forecastio==1.4.0'] _LOGGER = logging.getLogger(__name__) @@ -131,7 +131,11 @@ def ozone(self): @property def pressure(self): """Return the pressure.""" - return self._ds_currently.get('pressure') + pressure = self._ds_currently.get('pressure') + if 'us' in self._dark_sky.units: + return round( + convert_pressure(pressure, PRESSURE_HPA, PRESSURE_INHG), 2) + return pressure @property def visibility(self): diff --git a/homeassistant/components/openweathermap/weather.py b/homeassistant/components/openweathermap/weather.py index 58016dd3e2ccb9..8a37bc97575180 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -10,10 +10,10 @@ ATTR_FORECAST_WIND_SPEED, PLATFORM_SCHEMA, WeatherEntity) from homeassistant.const import ( CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_MODE, CONF_NAME, - STATE_UNKNOWN, TEMP_CELSIUS) + PRESSURE_HPA, PRESSURE_INHG, STATE_UNKNOWN, TEMP_CELSIUS) import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle - +from homeassistant.util.pressure import convert as convert_pressure REQUIREMENTS = ['pyowm==2.10.0'] _LOGGER = logging.getLogger(__name__) @@ -114,7 +114,11 @@ def temperature_unit(self): @property def pressure(self): """Return the pressure.""" - return self.data.get_pressure().get('press') + pressure = self.data.get_pressure().get('press') + if self.hass.config.units.name == 'imperial': + return round( + convert_pressure(pressure, PRESSURE_HPA, PRESSURE_INHG), 2) + return pressure @property def humidity(self): diff --git a/homeassistant/const.py b/homeassistant/const.py index f825e066f76daa..2d2f00f1e16072 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -343,6 +343,13 @@ LENGTH_YARD = 'yd' # type: str LENGTH_MILES = 'mi' # type: str +# Pressure units +PRESSURE_PA = 'Pa' # type: str +PRESSURE_HPA = 'hPa' # type: str +PRESSURE_MBAR = 'mbar' # type: str +PRESSURE_INHG = 'inHg' # type: str +PRESSURE_PSI = 'psi' # type: str + # Volume units VOLUME_LITERS = 'L' # type: str VOLUME_MILLILITERS = 'mL' # type: str @@ -455,6 +462,7 @@ LENGTH = 'length' # type: str MASS = 'mass' # type: str +PRESSURE = 'pressure' # type: str VOLUME = 'volume' # type: str TEMPERATURE = 'temperature' # type: str SPEED_MS = 'speed_ms' # type: str diff --git a/homeassistant/util/pressure.py b/homeassistant/util/pressure.py new file mode 100644 index 00000000000000..ecfa6344d29291 --- /dev/null +++ b/homeassistant/util/pressure.py @@ -0,0 +1,51 @@ +"""Pressure util functions.""" + +import logging +from numbers import Number + +from homeassistant.const import ( + PRESSURE_PA, + PRESSURE_HPA, + PRESSURE_MBAR, + PRESSURE_INHG, + PRESSURE_PSI, + UNIT_NOT_RECOGNIZED_TEMPLATE, + PRESSURE, +) + +_LOGGER = logging.getLogger(__name__) + +VALID_UNITS = [ + PRESSURE_PA, + PRESSURE_HPA, + PRESSURE_MBAR, + PRESSURE_INHG, + PRESSURE_PSI, +] + +UNIT_CONVERSION = { + PRESSURE_PA: 1, + PRESSURE_HPA: 1 / 100, + PRESSURE_MBAR: 1 / 100, + PRESSURE_INHG: 1 / 3386.389, + PRESSURE_PSI: 1 / 6894.757, +} + + +def convert(value: float, unit_1: str, unit_2: str) -> float: + """Convert one unit of measurement to another.""" + if unit_1 not in VALID_UNITS: + raise ValueError( + UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_1, PRESSURE)) + if unit_2 not in VALID_UNITS: + raise ValueError( + UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_2, PRESSURE)) + + if not isinstance(value, Number): + raise TypeError('{} is not of numeric type'.format(value)) + + if unit_1 == unit_2 or unit_1 not in VALID_UNITS: + return value + + pascals = value / UNIT_CONVERSION[unit_1] + return pascals * UNIT_CONVERSION[unit_2] diff --git a/homeassistant/util/unit_system.py b/homeassistant/util/unit_system.py index 5f6d202b5e940a..8e506dfca2ea2f 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -5,27 +5,19 @@ from numbers import Number from homeassistant.const import ( - TEMP_CELSIUS, TEMP_FAHRENHEIT, LENGTH_CENTIMETERS, LENGTH_METERS, - LENGTH_KILOMETERS, LENGTH_INCHES, LENGTH_FEET, LENGTH_YARD, LENGTH_MILES, - VOLUME_LITERS, VOLUME_MILLILITERS, VOLUME_GALLONS, VOLUME_FLUID_OUNCE, + TEMP_CELSIUS, TEMP_FAHRENHEIT, LENGTH_MILES, LENGTH_KILOMETERS, + PRESSURE_PA, PRESSURE_PSI, VOLUME_LITERS, VOLUME_GALLONS, MASS_GRAMS, MASS_KILOGRAMS, MASS_OUNCES, MASS_POUNDS, - CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, LENGTH, MASS, VOLUME, - TEMPERATURE, UNIT_NOT_RECOGNIZED_TEMPLATE) + CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, LENGTH, MASS, PRESSURE, + VOLUME, TEMPERATURE, UNIT_NOT_RECOGNIZED_TEMPLATE) from homeassistant.util import temperature as temperature_util from homeassistant.util import distance as distance_util +from homeassistant.util import pressure as pressure_util from homeassistant.util import volume as volume_util _LOGGER = logging.getLogger(__name__) -LENGTH_UNITS = [ - LENGTH_MILES, - LENGTH_YARD, - LENGTH_FEET, - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_METERS, - LENGTH_CENTIMETERS, -] +LENGTH_UNITS = distance_util.VALID_UNITS MASS_UNITS = [ MASS_POUNDS, @@ -34,12 +26,9 @@ MASS_GRAMS, ] -VOLUME_UNITS = [ - VOLUME_GALLONS, - VOLUME_FLUID_OUNCE, - VOLUME_LITERS, - VOLUME_MILLILITERS, -] +PRESSURE_UNITS = pressure_util.VALID_UNITS + +VOLUME_UNITS = volume_util.VALID_UNITS TEMPERATURE_UNITS = [ TEMP_FAHRENHEIT, @@ -57,6 +46,8 @@ def is_valid_unit(unit: str, unit_type: str) -> bool: units = MASS_UNITS elif unit_type == VOLUME: units = VOLUME_UNITS + elif unit_type == PRESSURE: + units = PRESSURE_UNITS else: return False @@ -67,7 +58,7 @@ class UnitSystem: """A container for units of measure.""" def __init__(self, name: str, temperature: str, length: str, - volume: str, mass: str) -> None: + volume: str, mass: str, pressure: str) -> None: """Initialize the unit system object.""" errors = \ ', '.join(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit, unit_type) @@ -75,7 +66,8 @@ def __init__(self, name: str, temperature: str, length: str, (temperature, TEMPERATURE), (length, LENGTH), (volume, VOLUME), - (mass, MASS), ] + (mass, MASS), + (pressure, PRESSURE), ] if not is_valid_unit(unit, unit_type)) # type: str if errors: @@ -85,6 +77,7 @@ def __init__(self, name: str, temperature: str, length: str, self.temperature_unit = temperature self.length_unit = length self.mass_unit = mass + self.pressure_unit = pressure self.volume_unit = volume @property @@ -109,6 +102,14 @@ def length(self, length: Optional[float], from_unit: str) -> float: return distance_util.convert(length, from_unit, self.length_unit) + def pressure(self, pressure: Optional[float], from_unit: str) -> float: + """Convert the given pressure to this unit system.""" + if not isinstance(pressure, Number): + raise TypeError('{} is not a numeric value.'.format(str(pressure))) + + return pressure_util.convert(pressure, from_unit, + self.pressure_unit) + def volume(self, volume: Optional[float], from_unit: str) -> float: """Convert the given volume to this unit system.""" if not isinstance(volume, Number): @@ -121,13 +122,16 @@ def as_dict(self) -> dict: return { LENGTH: self.length_unit, MASS: self.mass_unit, + PRESSURE: self.pressure_unit, TEMPERATURE: self.temperature_unit, VOLUME: self.volume_unit } METRIC_SYSTEM = UnitSystem(CONF_UNIT_SYSTEM_METRIC, TEMP_CELSIUS, - LENGTH_KILOMETERS, VOLUME_LITERS, MASS_GRAMS) + LENGTH_KILOMETERS, VOLUME_LITERS, MASS_GRAMS, + PRESSURE_PA) IMPERIAL_SYSTEM = UnitSystem(CONF_UNIT_SYSTEM_IMPERIAL, TEMP_FAHRENHEIT, - LENGTH_MILES, VOLUME_GALLONS, MASS_POUNDS) + LENGTH_MILES, VOLUME_GALLONS, MASS_POUNDS, + PRESSURE_PSI) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 3febd4037ad099..73fe36af26d46a 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -15,6 +15,7 @@ LENGTH_METERS, TEMP_CELSIUS, MASS_GRAMS, + PRESSURE_PA, VOLUME_LITERS, MATCH_ALL, ) @@ -33,7 +34,7 @@ def setUp(self): self.hass = get_test_home_assistant() self.hass.config.units = UnitSystem('custom', TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, - MASS_GRAMS) + MASS_GRAMS, PRESSURE_PA) # pylint: disable=invalid-name def tearDown(self): diff --git a/tests/util/test_pressure.py b/tests/util/test_pressure.py new file mode 100644 index 00000000000000..a3e6efb37541a8 --- /dev/null +++ b/tests/util/test_pressure.py @@ -0,0 +1,66 @@ +"""Test homeassistant pressure utility functions.""" +import unittest +import pytest + +from homeassistant.const import (PRESSURE_PA, PRESSURE_HPA, PRESSURE_MBAR, + PRESSURE_INHG, PRESSURE_PSI) +import homeassistant.util.pressure as pressure_util + +INVALID_SYMBOL = 'bob' +VALID_SYMBOL = PRESSURE_PA + + +class TestPressureUtil(unittest.TestCase): + """Test the pressure utility functions.""" + + def test_convert_same_unit(self): + """Test conversion from any unit to same unit.""" + assert pressure_util.convert(2, PRESSURE_PA, PRESSURE_PA) == 2 + assert pressure_util.convert(3, PRESSURE_HPA, PRESSURE_HPA) == 3 + assert pressure_util.convert(4, PRESSURE_MBAR, PRESSURE_MBAR) == 4 + assert pressure_util.convert(5, PRESSURE_INHG, PRESSURE_INHG) == 5 + + def test_convert_invalid_unit(self): + """Test exception is thrown for invalid units.""" + with pytest.raises(ValueError): + pressure_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL) + + with pytest.raises(ValueError): + pressure_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL) + + def test_convert_nonnumeric_value(self): + """Test exception is thrown for nonnumeric type.""" + with pytest.raises(TypeError): + pressure_util.convert('a', PRESSURE_HPA, PRESSURE_INHG) + + def test_convert_from_hpascals(self): + """Test conversion from hPA to other units.""" + hpascals = 1000 + self.assertAlmostEqual( + pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PSI), + 14.5037743897) + self.assertAlmostEqual( + pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_INHG), + 29.5299801647) + self.assertAlmostEqual( + pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_PA), + 100000) + self.assertAlmostEqual( + pressure_util.convert(hpascals, PRESSURE_HPA, PRESSURE_MBAR), + 1000) + + def test_convert_from_inhg(self): + """Test conversion from inHg to other units.""" + inhg = 30 + self.assertAlmostEqual( + pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PSI), + 14.7346266155) + self.assertAlmostEqual( + pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_HPA), + 1015.9167) + self.assertAlmostEqual( + pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_PA), + 101591.67) + self.assertAlmostEqual( + pressure_util.convert(inhg, PRESSURE_INHG, PRESSURE_MBAR), + 1015.9167) diff --git a/tests/util/test_unit_system.py b/tests/util/test_unit_system.py index 31b2d49b4eca3f..533ce3c0a15373 100644 --- a/tests/util/test_unit_system.py +++ b/tests/util/test_unit_system.py @@ -10,10 +10,12 @@ LENGTH_METERS, LENGTH_KILOMETERS, MASS_GRAMS, + PRESSURE_PA, VOLUME_LITERS, TEMP_CELSIUS, LENGTH, MASS, + PRESSURE, TEMPERATURE, VOLUME ) @@ -30,19 +32,23 @@ def test_invalid_units(self): """Test errors are raised when invalid units are passed in.""" with pytest.raises(ValueError): UnitSystem(SYSTEM_NAME, INVALID_UNIT, LENGTH_METERS, VOLUME_LITERS, - MASS_GRAMS) + MASS_GRAMS, PRESSURE_PA) with pytest.raises(ValueError): UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, INVALID_UNIT, VOLUME_LITERS, - MASS_GRAMS) + MASS_GRAMS, PRESSURE_PA) with pytest.raises(ValueError): UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, INVALID_UNIT, - MASS_GRAMS) + MASS_GRAMS, PRESSURE_PA) with pytest.raises(ValueError): UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, - INVALID_UNIT) + INVALID_UNIT, PRESSURE_PA) + + with pytest.raises(ValueError): + UnitSystem(SYSTEM_NAME, TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, + MASS_GRAMS, INVALID_UNIT) def test_invalid_value(self): """Test no conversion happens if value is non-numeric.""" @@ -50,6 +56,10 @@ def test_invalid_value(self): METRIC_SYSTEM.length('25a', LENGTH_KILOMETERS) with pytest.raises(TypeError): METRIC_SYSTEM.temperature('50K', TEMP_CELSIUS) + with pytest.raises(TypeError): + METRIC_SYSTEM.volume('50L', VOLUME_LITERS) + with pytest.raises(TypeError): + METRIC_SYSTEM.pressure('50Pa', PRESSURE_PA) def test_as_dict(self): """Test that the as_dict() method returns the expected dictionary.""" @@ -57,7 +67,8 @@ def test_as_dict(self): LENGTH: LENGTH_KILOMETERS, TEMPERATURE: TEMP_CELSIUS, VOLUME: VOLUME_LITERS, - MASS: MASS_GRAMS + MASS: MASS_GRAMS, + PRESSURE: PRESSURE_PA } assert expected == METRIC_SYSTEM.as_dict() @@ -108,12 +119,39 @@ def test_length_to_imperial(self): assert 3.106855 == \ IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit) + def test_pressure_same_unit(self): + """Test no conversion happens if to unit is same as from unit.""" + assert 5 == \ + METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit) + + def test_pressure_unknown_unit(self): + """Test no conversion happens if unknown unit.""" + with pytest.raises(ValueError): + METRIC_SYSTEM.pressure(5, 'K') + + def test_pressure_to_metric(self): + """Test pressure conversion to metric system.""" + assert 25 == \ + METRIC_SYSTEM.pressure(25, METRIC_SYSTEM.pressure_unit) + self.assertAlmostEqual( + METRIC_SYSTEM.pressure(14.7, IMPERIAL_SYSTEM.pressure_unit), + 101352.932, places=1) + + def test_pressure_to_imperial(self): + """Test pressure conversion to imperial system.""" + assert 77 == \ + IMPERIAL_SYSTEM.pressure(77, IMPERIAL_SYSTEM.pressure_unit) + self.assertAlmostEqual( + IMPERIAL_SYSTEM.pressure(101352.932, METRIC_SYSTEM.pressure_unit), + 14.7, places=4) + def test_properties(self): """Test the unit properties are returned as expected.""" assert LENGTH_KILOMETERS == METRIC_SYSTEM.length_unit assert TEMP_CELSIUS == METRIC_SYSTEM.temperature_unit assert MASS_GRAMS == METRIC_SYSTEM.mass_unit assert VOLUME_LITERS == METRIC_SYSTEM.volume_unit + assert PRESSURE_PA == METRIC_SYSTEM.pressure_unit def test_is_metric(self): """Test the is metric flag.""" From 89f82031630f1d8e6662dffa60e60d4db00ec090 Mon Sep 17 00:00:00 2001 From: Yu Date: Mon, 25 Mar 2019 01:56:17 +0800 Subject: [PATCH 116/605] Fix xiaomi aqara cube with lumi.acpartner.v3 gateway (#22130) --- homeassistant/components/xiaomi_aqara/binary_sensor.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/binary_sensor.py b/homeassistant/components/xiaomi_aqara/binary_sensor.py index 7eb72e91eef714..56818c51b817b3 100644 --- a/homeassistant/components/xiaomi_aqara/binary_sensor.py +++ b/homeassistant/components/xiaomi_aqara/binary_sensor.py @@ -476,18 +476,24 @@ def parse_data(self, data, raw_data): self._last_action = data[self._data_key] if 'rotate' in data: + action_value = float(data['rotate'] + if isinstance(data['rotate'], int) + else data['rotate'].replace(",", ".")) self._hass.bus.fire('xiaomi_aqara.cube_action', { 'entity_id': self.entity_id, 'action_type': 'rotate', - 'action_value': float(data['rotate'].replace(",", ".")) + 'action_value': action_value }) self._last_action = 'rotate' if 'rotate_degree' in data: + action_value = float(data['rotate_degree'] + if isinstance(data['rotate_degree'], int) + else data['rotate_degree'].replace(",", ".")) self._hass.bus.fire('xiaomi_aqara.cube_action', { 'entity_id': self.entity_id, 'action_type': 'rotate', - 'action_value': float(data['rotate_degree'].replace(",", ".")) + 'action_value': action_value }) self._last_action = 'rotate' From 8d1cf553de9d170b36e95ef81b99fdd81b867d34 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 24 Mar 2019 19:27:32 +0100 Subject: [PATCH 117/605] Support deCONZ library with exception handling (#21952) --- homeassistant/components/deconz/__init__.py | 5 +- .../components/deconz/config_flow.py | 45 ++++++++--- homeassistant/components/deconz/errors.py | 18 +++++ homeassistant/components/deconz/gateway.py | 42 +++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/deconz/test_climate.py | 13 ++-- tests/components/deconz/test_config_flow.py | 77 ++++++++++++------- tests/components/deconz/test_gateway.py | 37 +++++++-- tests/components/deconz/test_init.py | 14 +++- 10 files changed, 190 insertions(+), 65 deletions(-) create mode 100644 homeassistant/components/deconz/errors.py diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index d107cba8f7b71e..957bb5691108aa 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,7 +12,7 @@ from .const import DEFAULT_PORT, DOMAIN, _LOGGER from .gateway import DeconzGateway -REQUIREMENTS = ['pydeconz==52'] +REQUIREMENTS = ['pydeconz==53'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -124,8 +124,7 @@ async def async_refresh_devices(call): scenes = set(gateway.api.scenes.keys()) sensors = set(gateway.api.sensors.keys()) - if not await gateway.api.async_load_parameters(): - return + await gateway.api.async_load_parameters() gateway.async_add_device_callback( 'group', [group diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 8f90f303fcaad6..cabb5b46ece5bc 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -1,4 +1,6 @@ """Config flow to configure deCONZ component.""" +import asyncio +import async_timeout import voluptuous as vol from homeassistant import config_entries @@ -32,15 +34,12 @@ def __init__(self): self.deconz_config = {} async def async_step_user(self, user_input=None): - """Handle a flow initialized by the user.""" - return await self.async_step_init(user_input) - - async def async_step_init(self, user_input=None): """Handle a deCONZ config flow start. Only allows one instance to be set up. If only one bridge is found go to link step. If more than one bridge is found let user choose bridge to link. + If no bridge is found allow user to manually input configuration. """ from pydeconz.utils import async_discovery @@ -52,11 +51,18 @@ async def async_step_init(self, user_input=None): if bridge[CONF_HOST] == user_input[CONF_HOST]: self.deconz_config = bridge return await self.async_step_link() + self.deconz_config = user_input return await self.async_step_link() session = aiohttp_client.async_get_clientsession(self.hass) - self.bridges = await async_discovery(session) + + try: + with async_timeout.timeout(10): + self.bridges = await async_discovery(session) + + except asyncio.TimeoutError: + self.bridges = [] if len(self.bridges) == 1: self.deconz_config = self.bridges[0] @@ -64,8 +70,10 @@ async def async_step_init(self, user_input=None): if len(self.bridges) > 1: hosts = [] + for bridge in self.bridges: hosts.append(bridge[CONF_HOST]) + return self.async_show_form( step_id='init', data_schema=vol.Schema({ @@ -74,7 +82,7 @@ async def async_step_init(self, user_input=None): ) return self.async_show_form( - step_id='user', + step_id='init', data_schema=vol.Schema({ vol.Required(CONF_HOST): str, vol.Required(CONF_PORT, default=DEFAULT_PORT): int, @@ -83,18 +91,27 @@ async def async_step_init(self, user_input=None): async def async_step_link(self, user_input=None): """Attempt to link with the deCONZ bridge.""" + from pydeconz.errors import ResponseError, RequestError from pydeconz.utils import async_get_api_key errors = {} if user_input is not None: if configured_hosts(self.hass): return self.async_abort(reason='one_instance_only') + session = aiohttp_client.async_get_clientsession(self.hass) - api_key = await async_get_api_key(session, **self.deconz_config) - if api_key: + + try: + with async_timeout.timeout(10): + api_key = await async_get_api_key( + session, **self.deconz_config) + + except (ResponseError, RequestError, asyncio.TimeoutError): + errors['base'] = 'no_key' + + else: self.deconz_config[CONF_API_KEY] = api_key return await self.async_step_options() - errors['base'] = 'no_key' return self.async_show_form( step_id='link', @@ -117,8 +134,14 @@ async def async_step_options(self, user_input=None): if CONF_BRIDGEID not in self.deconz_config: session = aiohttp_client.async_get_clientsession(self.hass) - self.deconz_config[CONF_BRIDGEID] = await async_get_bridgeid( - session, **self.deconz_config) + try: + with async_timeout.timeout(10): + self.deconz_config[CONF_BRIDGEID] = \ + await async_get_bridgeid( + session, **self.deconz_config) + + except asyncio.TimeoutError: + return self.async_abort(reason='no_bridges') return self.async_create_entry( title='deCONZ-' + self.deconz_config[CONF_BRIDGEID], diff --git a/homeassistant/components/deconz/errors.py b/homeassistant/components/deconz/errors.py new file mode 100644 index 00000000000000..be13e579ce0950 --- /dev/null +++ b/homeassistant/components/deconz/errors.py @@ -0,0 +1,18 @@ +"""Errors for the deCONZ component.""" +from homeassistant.exceptions import HomeAssistantError + + +class DeconzException(HomeAssistantError): + """Base class for deCONZ exceptions.""" + + +class AlreadyConfigured(DeconzException): + """Gateway is already configured.""" + + +class AuthenticationRequired(DeconzException): + """Unknown error occurred.""" + + +class CannotConnect(DeconzException): + """Unable to connect to the gateway.""" diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 829485e1e9245f..6629d4eec14557 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -1,6 +1,9 @@ """Representation of a deCONZ gateway.""" +import asyncio +import async_timeout + from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.const import CONF_EVENT, CONF_ID +from homeassistant.const import CONF_EVENT, CONF_HOST, CONF_ID from homeassistant.core import EventOrigin, callback from homeassistant.helpers import aiohttp_client from homeassistant.helpers.dispatcher import ( @@ -10,6 +13,7 @@ from .const import ( _LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, NEW_DEVICE, NEW_SENSOR, SUPPORTED_PLATFORMS) +from .errors import AuthenticationRequired, CannotConnect class DeconzGateway: @@ -26,18 +30,23 @@ def __init__(self, hass, config_entry): self.events = [] self.listeners = [] - async def async_setup(self, tries=0): + async def async_setup(self): """Set up a deCONZ gateway.""" hass = self.hass - self.api = await get_gateway( - hass, self.config_entry.data, self.async_add_device_callback, - self.async_connection_status_callback - ) + try: + self.api = await get_gateway( + hass, self.config_entry.data, self.async_add_device_callback, + self.async_connection_status_callback + ) - if not self.api: + except CannotConnect: raise ConfigEntryNotReady + except Exception: # pylint: disable=broad-except + _LOGGER.error('Error connecting with deCONZ gateway.') + return False + for component in SUPPORTED_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup( @@ -113,17 +122,26 @@ async def async_reset(self): async def get_gateway(hass, config, async_add_device_callback, async_connection_status_callback): """Create a gateway object and verify configuration.""" - from pydeconz import DeconzSession + from pydeconz import DeconzSession, errors session = aiohttp_client.async_get_clientsession(hass) + deconz = DeconzSession(hass.loop, session, **config, async_add_device=async_add_device_callback, connection_status=async_connection_status_callback) - result = await deconz.async_load_parameters() - - if result: + try: + with async_timeout.timeout(10): + await deconz.async_load_parameters() return deconz - return result + + except errors.Unauthorized: + _LOGGER.warning("Invalid key for deCONZ at %s.", config[CONF_HOST]) + raise AuthenticationRequired + + except (asyncio.TimeoutError, errors.RequestError): + _LOGGER.error( + "Error connecting to deCONZ gateway at %s", config[CONF_HOST]) + raise CannotConnect class DeconzEvent: diff --git a/requirements_all.txt b/requirements_all.txt index 14e845074e6981..15ae90ebe0b986 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1001,7 +1001,7 @@ pydaikin==1.1.0 pydanfossair==0.0.7 # homeassistant.components.deconz -pydeconz==52 +pydeconz==53 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 731f7fa9d22115..05a14e18fc08c1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -200,7 +200,7 @@ pyHS100==0.3.4 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==52 +pydeconz==53 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py index fa274f1d676cab..953bb776419887 100644 --- a/tests/components/deconz/test_climate.py +++ b/tests/components/deconz/test_climate.py @@ -46,11 +46,14 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): """Load the deCONZ sensor platform.""" from pydeconz import DeconzSession - session = Mock(put=asynctest.CoroutineMock( - return_value=Mock(status=200, - json=asynctest.CoroutineMock(), - text=asynctest.CoroutineMock(), - ) + response = Mock( + status=200, json=asynctest.CoroutineMock(), + text=asynctest.CoroutineMock()) + response.content_type = 'application/json' + + session = Mock( + put=asynctest.CoroutineMock( + return_value=response ) ) diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 9e1d6a2fca1e64..20c74a8288310d 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -1,7 +1,8 @@ """Tests for deCONZ config flow.""" -import pytest +from unittest.mock import patch + +import asyncio -import voluptuous as vol from homeassistant.components.deconz import config_flow from tests.common import MockConfigEntry @@ -12,15 +13,17 @@ async def test_flow_works(hass, aioclient_mock): """Test that config flow works.""" aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[ {'id': 'id', 'internalipaddress': '1.2.3.4', 'internalport': 80} - ]) + ], headers={'content-type': 'application/json'}) aioclient_mock.post('http://1.2.3.4:80/api', json=[ {"success": {"username": "1234567890ABCDEF"}} - ]) + ], headers={'content-type': 'application/json'}) flow = config_flow.DeconzFlowHandler() flow.hass = hass + await flow.async_step_user() await flow.async_step_link(user_input={}) + result = await flow.async_step_options( user_input={'allow_clip_sensor': True, 'allow_deconz_groups': True}) @@ -41,35 +44,53 @@ async def test_flow_already_registered_bridge(hass): MockConfigEntry(domain='deconz', data={ 'host': '1.2.3.4' }).add_to_hass(hass) + flow = config_flow.DeconzFlowHandler() flow.hass = hass - result = await flow.async_step_init() + result = await flow.async_step_user() assert result['type'] == 'abort' +async def test_flow_bridge_discovery_fails(hass, aioclient_mock): + """Test config flow works when discovery fails.""" + flow = config_flow.DeconzFlowHandler() + flow.hass = hass + + with patch('pydeconz.utils.async_discovery', + side_effect=asyncio.TimeoutError): + result = await flow.async_step_user() + + assert result['type'] == 'form' + assert result['step_id'] == 'init' + + async def test_flow_no_discovered_bridges(hass, aioclient_mock): """Test config flow discovers no bridges.""" - aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[]) + aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[], + headers={'content-type': 'application/json'}) + flow = config_flow.DeconzFlowHandler() flow.hass = hass - result = await flow.async_step_init() + result = await flow.async_step_user() assert result['type'] == 'form' - assert result['step_id'] == 'user' + assert result['step_id'] == 'init' async def test_flow_one_bridge_discovered(hass, aioclient_mock): """Test config flow discovers one bridge.""" aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[ {'id': 'id', 'internalipaddress': '1.2.3.4', 'internalport': 80} - ]) + ], headers={'content-type': 'application/json'}) + flow = config_flow.DeconzFlowHandler() flow.hass = hass - result = await flow.async_step_init() + result = await flow.async_step_user() assert result['type'] == 'form' assert result['step_id'] == 'link' + assert flow.deconz_config['host'] == '1.2.3.4' async def test_flow_two_bridges_discovered(hass, aioclient_mock): @@ -77,19 +98,14 @@ async def test_flow_two_bridges_discovered(hass, aioclient_mock): aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[ {'id': 'id1', 'internalipaddress': '1.2.3.4', 'internalport': 80}, {'id': 'id2', 'internalipaddress': '5.6.7.8', 'internalport': 80} - ]) + ], headers={'content-type': 'application/json'}) + flow = config_flow.DeconzFlowHandler() flow.hass = hass - result = await flow.async_step_init() - assert result['type'] == 'form' - assert result['step_id'] == 'init' - - with pytest.raises(vol.Invalid): - assert result['data_schema']({'host': '0.0.0.0'}) - - result['data_schema']({'host': '1.2.3.4'}) - result['data_schema']({'host': '5.6.7.8'}) + result = await flow.async_step_user() + assert result['data_schema']({'host': '1.2.3.4'}) + assert result['data_schema']({'host': '5.6.7.8'}) async def test_flow_two_bridges_selection(hass, aioclient_mock): @@ -101,7 +117,7 @@ async def test_flow_two_bridges_selection(hass, aioclient_mock): {'bridgeid': 'id2', 'host': '5.6.7.8', 'port': 80} ] - result = await flow.async_step_init(user_input={'host': '1.2.3.4'}) + result = await flow.async_step_user(user_input={'host': '1.2.3.4'}) assert result['type'] == 'form' assert result['step_id'] == 'link' assert flow.deconz_config['host'] == '1.2.3.4' @@ -110,25 +126,28 @@ async def test_flow_two_bridges_selection(hass, aioclient_mock): async def test_flow_manual_configuration(hass, aioclient_mock): """Test config flow with manual input.""" aioclient_mock.get(pydeconz.utils.URL_DISCOVER, json=[]) + flow = config_flow.DeconzFlowHandler() flow.hass = hass user_input = {'host': '1.2.3.4', 'port': 80} - result = await flow.async_step_init(user_input) + result = await flow.async_step_user(user_input) assert result['type'] == 'form' assert result['step_id'] == 'link' assert flow.deconz_config == user_input -async def test_link_no_api_key(hass, aioclient_mock): +async def test_link_no_api_key(hass): """Test config flow should abort if no API key was possible to retrieve.""" - aioclient_mock.post('http://1.2.3.4:80/api', json=[]) flow = config_flow.DeconzFlowHandler() flow.hass = hass flow.deconz_config = {'host': '1.2.3.4', 'port': 80} - result = await flow.async_step_link(user_input={}) + with patch('pydeconz.utils.async_get_api_key', + side_effect=pydeconz.errors.ResponseError): + result = await flow.async_step_link(user_input={}) + assert result['type'] == 'form' assert result['step_id'] == 'link' assert result['errors'] == {'base': 'no_key'} @@ -143,6 +162,7 @@ async def test_link_already_registered_bridge(hass): MockConfigEntry(domain='deconz', data={ 'host': '1.2.3.4' }).add_to_hass(hass) + flow = config_flow.DeconzFlowHandler() flow.hass = hass flow.deconz_config = {'host': '1.2.3.4', 'port': 80} @@ -155,6 +175,7 @@ async def test_bridge_discovery(hass): """Test a bridge being discovered.""" flow = config_flow.DeconzFlowHandler() flow.hass = hass + result = await flow.async_step_discovery({ 'host': '1.2.3.4', 'port': 80, @@ -222,14 +243,18 @@ async def test_import_with_api_key(hass): async def test_options(hass, aioclient_mock): """Test that options work and that bridgeid can be requested.""" aioclient_mock.get('http://1.2.3.4:80/api/1234567890ABCDEF/config', - json={"bridgeid": "id"}) + json={"bridgeid": "id"}, + headers={'content-type': 'application/json'}) + flow = config_flow.DeconzFlowHandler() flow.hass = hass flow.deconz_config = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} + result = await flow.async_step_options( user_input={'allow_clip_sensor': False, 'allow_deconz_groups': False}) + assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' assert result['data'] == { diff --git a/tests/components/deconz/test_gateway.py b/tests/components/deconz/test_gateway.py index d73f225b2acfb7..6006ff668986ab 100644 --- a/tests/components/deconz/test_gateway.py +++ b/tests/components/deconz/test_gateway.py @@ -4,10 +4,13 @@ import pytest from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.components.deconz import gateway +from homeassistant.components.deconz import errors, gateway from tests.common import mock_coro +import pydeconz + + ENTRY_CONFIG = { "host": "1.2.3.4", "port": 80, @@ -62,11 +65,25 @@ async def test_gateway_retry(): deconz_gateway = gateway.DeconzGateway(hass, entry) with patch.object( - gateway, 'get_gateway', return_value=mock_coro(False) - ), pytest.raises(ConfigEntryNotReady): + gateway, 'get_gateway', side_effect=errors.CannotConnect), \ + pytest.raises(ConfigEntryNotReady): await deconz_gateway.async_setup() +async def test_gateway_setup_fails(): + """Retry setup.""" + hass = Mock() + entry = Mock() + entry.data = ENTRY_CONFIG + + deconz_gateway = gateway.DeconzGateway(hass, entry) + + with patch.object(gateway, 'get_gateway', side_effect=Exception): + result = await deconz_gateway.async_setup() + + assert not result + + async def test_connection_status(hass): """Make sure that connection status triggers a dispatcher send.""" entry = Mock() @@ -170,10 +187,20 @@ async def test_get_gateway(hass): assert await gateway.get_gateway(hass, ENTRY_CONFIG, Mock(), Mock()) -async def test_get_gateway_fails(hass): +async def test_get_gateway_fails_unauthorized(hass): + """Failed call.""" + with patch('pydeconz.DeconzSession.async_load_parameters', + side_effect=pydeconz.errors.Unauthorized), \ + pytest.raises(errors.AuthenticationRequired): + assert await gateway.get_gateway( + hass, ENTRY_CONFIG, Mock(), Mock()) is False + + +async def test_get_gateway_fails_cannot_connect(hass): """Failed call.""" with patch('pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(False)): + side_effect=pydeconz.errors.RequestError), \ + pytest.raises(errors.CannotConnect): assert await gateway.get_gateway( hass, ENTRY_CONFIG, Mock(), Mock()) is False diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index cbba47eb431548..e0afadccc81581 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -1,6 +1,7 @@ """Test deCONZ component setup process.""" from unittest.mock import Mock, patch +import asyncio import pytest import voluptuous as vol @@ -76,13 +77,22 @@ async def test_setup_entry_already_registered_bridge(hass): assert await deconz.async_setup_entry(hass, {}) is False +async def test_setup_entry_fails(hass): + """Test setup entry fails if deCONZ is not available.""" + entry = Mock() + entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} + with patch('pydeconz.DeconzSession.async_load_parameters', + side_effect=Exception): + await deconz.async_setup_entry(hass, entry) + + async def test_setup_entry_no_available_bridge(hass): """Test setup entry fails if deCONZ is not available.""" entry = Mock() entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} with patch( 'pydeconz.DeconzSession.async_load_parameters', - return_value=mock_coro(False) + side_effect=asyncio.TimeoutError ), pytest.raises(ConfigEntryNotReady): await deconz.async_setup_entry(hass, entry) @@ -185,6 +195,7 @@ async def test_service_refresh_devices(hass): }) entry.add_to_hass(hass) mock_registry = Mock() + with patch.object(deconz, 'DeconzGateway') as mock_gateway, \ patch('homeassistant.helpers.device_registry.async_get_registry', return_value=mock_coro(mock_registry)): @@ -196,6 +207,7 @@ async def test_service_refresh_devices(hass): await hass.services.async_call( 'deconz', 'device_refresh', service_data={}) await hass.async_block_till_done() + with patch.object(hass.data[deconz.DOMAIN].api, 'async_load_parameters', return_value=mock_coro(False)): await hass.services.async_call( From eabb68ad7d5399f386a317126f4d48faf0067124 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 24 Mar 2019 20:00:29 +0100 Subject: [PATCH 118/605] Do not warn when creating an empty database (#22343) --- homeassistant/components/recorder/migration.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 972862e7a9c0da..f81dd9e736f4dd 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -19,6 +19,11 @@ def migrate_schema(instance): SchemaChanges.change_id.desc()).first() current_version = getattr(res, 'schema_version', None) + if current_version is None: + current_version = _inspect_schema_version(instance.engine, session) + _LOGGER.debug("No schema version found. Inspected version: %s", + current_version) + if current_version == SCHEMA_VERSION: # Clean up if old migration left file if os.path.isfile(progress_path): @@ -32,11 +37,6 @@ def migrate_schema(instance): _LOGGER.warning("Database is about to upgrade. Schema version: %s", current_version) - if current_version is None: - current_version = _inspect_schema_version(instance.engine, session) - _LOGGER.debug("No schema version found. Inspected version: %s", - current_version) - try: for version in range(current_version, SCHEMA_VERSION): new_version = version + 1 From d5732c4dba13c7ed5090ff3026222f9963aa04a9 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Sun, 24 Mar 2019 20:14:35 +0100 Subject: [PATCH 119/605] Add color support to Philips Moonlight (#22204) --- homeassistant/components/xiaomi_miio/light.py | 99 ++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index ecf7b12e4ac1b8..ec07a557342cca 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -9,8 +9,9 @@ import voluptuous as vol from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_ENTITY_ID, DOMAIN, PLATFORM_SCHEMA, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, Light) + ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_COLOR_TEMP, ATTR_ENTITY_ID, DOMAIN, + PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, + Light) from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv @@ -774,7 +775,99 @@ def hs_color(self) -> tuple: @property def supported_features(self): """Return the supported features.""" - return SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP + return SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP + + async def async_turn_on(self, **kwargs): + """Turn the light on.""" + if ATTR_COLOR_TEMP in kwargs: + color_temp = kwargs[ATTR_COLOR_TEMP] + percent_color_temp = self.translate( + color_temp, self.max_mireds, + self.min_mireds, CCT_MIN, CCT_MAX) + + if ATTR_BRIGHTNESS in kwargs: + brightness = kwargs[ATTR_BRIGHTNESS] + percent_brightness = ceil(100 * brightness / 255.0) + + if ATTR_HS_COLOR in kwargs: + hs_color = kwargs[ATTR_HS_COLOR] + rgb = color.color_hs_to_RGB(*hs_color) + + if ATTR_BRIGHTNESS in kwargs and ATTR_HS_COLOR in kwargs: + _LOGGER.debug( + "Setting brightness and color: " + "%s %s%%, %s", + brightness, percent_brightness, rgb) + + result = await self._try_command( + "Setting brightness and color failed: " + "%s bri, %s color", + self._light.set_brightness_and_rgb, + percent_brightness, rgb) + + if result: + self._hs_color = hs_color + self._brightness = brightness + + elif ATTR_BRIGHTNESS in kwargs and ATTR_COLOR_TEMP in kwargs: + _LOGGER.debug( + "Setting brightness and color temperature: " + "%s %s%%, %s mireds, %s%% cct", + brightness, percent_brightness, + color_temp, percent_color_temp) + + result = await self._try_command( + "Setting brightness and color temperature failed: " + "%s bri, %s cct", + self._light.set_brightness_and_color_temperature, + percent_brightness, percent_color_temp) + + if result: + self._color_temp = color_temp + self._brightness = brightness + + elif ATTR_HS_COLOR in kwargs: + _LOGGER.debug( + "Setting color: %s", rgb) + + result = await self._try_command( + "Setting color failed: %s", + self._light.set_rgb, rgb) + + if result: + self._hs_color = hs_color + + elif ATTR_COLOR_TEMP in kwargs: + _LOGGER.debug( + "Setting color temperature: " + "%s mireds, %s%% cct", + color_temp, percent_color_temp) + + result = await self._try_command( + "Setting color temperature failed: %s cct", + self._light.set_color_temperature, percent_color_temp) + + if result: + self._color_temp = color_temp + + elif ATTR_BRIGHTNESS in kwargs: + brightness = kwargs[ATTR_BRIGHTNESS] + percent_brightness = ceil(100 * brightness / 255.0) + + _LOGGER.debug( + "Setting brightness: %s %s%%", + brightness, percent_brightness) + + result = await self._try_command( + "Setting brightness failed: %s", + self._light.set_brightness, percent_brightness) + + if result: + self._brightness = brightness + + else: + await self._try_command( + "Turning the light on failed.", self._light.on) async def async_update(self): """Fetch state from the device.""" From 7421156dfc5d4a17384a708c623c28ad73a737a6 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Sun, 24 Mar 2019 20:15:29 +0100 Subject: [PATCH 120/605] Add support for the power socket of the Xiaomi AC Partner V3 (#22205) --- .../components/xiaomi_miio/switch.py | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index f330030922d0a2..d1acce02e47ca9 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -36,6 +36,7 @@ 'chuangmi.plug.v2', 'chuangmi.plug.v3', 'chuangmi.plug.hmi205', + 'lumi.acpartner.v3', ]), }) @@ -150,6 +151,13 @@ async def async_setup_platform(hass, config, async_add_entities, device = XiaomiPlugGenericSwitch(name, plug, model, unique_id) devices.append(device) hass.data[DATA_KEY][host] = device + elif model in ['lumi.acpartner.v3']: + from miio import AirConditioningCompanionV3 + plug = AirConditioningCompanionV3(host, token) + device = XiaomiAirConditioningCompanionSwitch(name, plug, model, + unique_id) + devices.append(device) + hass.data[DATA_KEY][host] = device else: _LOGGER.error( 'Unsupported device found! Please create an issue at ' @@ -294,9 +302,7 @@ async def async_update(self): self._available = True self._state = state.is_on - self._state_attrs.update({ - ATTR_TEMPERATURE: state.temperature - }) + self._state_attrs[ATTR_TEMPERATURE] = state.temperature except DeviceException as ex: self._available = False @@ -342,9 +348,7 @@ def __init__(self, name, plug, model, unique_id): else: self._device_features = FEATURE_FLAGS_POWER_STRIP_V1 - self._state_attrs.update({ - ATTR_LOAD_POWER: None, - }) + self._state_attrs[ATTR_LOAD_POWER] = None if self._device_features & FEATURE_SET_POWER_MODE == 1: self._state_attrs[ATTR_POWER_MODE] = None @@ -418,13 +422,9 @@ def __init__(self, name, plug, model, unique_id, channel_usb): if self._model == MODEL_PLUG_V3: self._device_features = FEATURE_FLAGS_PLUG_V3 - self._state_attrs.update({ - ATTR_WIFI_LED: None, - }) + self._state_attrs[ATTR_WIFI_LED] = None if self._channel_usb is False: - self._state_attrs.update({ - ATTR_LOAD_POWER: None, - }) + self._state_attrs[ATTR_LOAD_POWER] = None async def async_turn_on(self, **kwargs): """Turn a channel on.""" @@ -471,9 +471,7 @@ async def async_update(self): else: self._state = state.is_on - self._state_attrs.update({ - ATTR_TEMPERATURE: state.temperature - }) + self._state_attrs[ATTR_TEMPERATURE] = state.temperature if state.wifi_led: self._state_attrs[ATTR_WIFI_LED] = state.wifi_led @@ -484,3 +482,55 @@ async def async_update(self): except DeviceException as ex: self._available = False _LOGGER.error("Got exception while fetching the state: %s", ex) + + +class XiaomiAirConditioningCompanionSwitch(XiaomiPlugGenericSwitch): + """Representation of a Xiaomi AirConditioning Companion.""" + + def __init__(self, name, plug, model, unique_id): + """Initialize the acpartner switch.""" + super().__init__(name, plug, model, unique_id) + + self._state_attrs.update({ + ATTR_TEMPERATURE: None, + ATTR_LOAD_POWER: None, + }) + + async def async_turn_on(self, **kwargs): + """Turn the socket on.""" + result = await self._try_command( + "Turning the socket on failed.", self._plug.socket_on) + + if result: + self._state = True + self._skip_update = True + + async def async_turn_off(self, **kwargs): + """Turn the socket off.""" + result = await self._try_command( + "Turning the socket off failed.", self._plug.socket_off) + + if result: + self._state = False + self._skip_update = True + + async def async_update(self): + """Fetch state from the device.""" + from miio import DeviceException + + # On state change the device doesn't provide the new state immediately. + if self._skip_update: + self._skip_update = False + return + + try: + state = await self.hass.async_add_executor_job(self._plug.status) + _LOGGER.debug("Got new state: %s", state) + + self._available = True + self._state = state.power_socket == 'on' + self._state_attrs[ATTR_LOAD_POWER] = state.load_power + + except DeviceException as ex: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) From 88df2e0ea5c527b0321ec2fa7efc5eaa2d6f2a68 Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Sun, 24 Mar 2019 17:08:59 -0700 Subject: [PATCH 121/605] Fix ps4 no creds with additional device (#22300) * Fix no creds with additional device. * Update config_flow.py --- homeassistant/components/ps4/config_flow.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/homeassistant/components/ps4/config_flow.py b/homeassistant/components/ps4/config_flow.py index 482c7383d89d76..148b0ae6d84c53 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -79,7 +79,11 @@ async def async_step_link(self, user_input=None): # If entry exists check that devices found aren't configured. if self.hass.config_entries.async_entries(DOMAIN): + creds = {} for entry in self.hass.config_entries.async_entries(DOMAIN): + # Retrieve creds from entry + creds['data'] = entry.data[CONF_TOKEN] + # Retrieve device data from entry conf_devices = entry.data['devices'] for c_device in conf_devices: if c_device['host'] in device_list: @@ -88,6 +92,11 @@ async def async_step_link(self, user_input=None): # If list is empty then all devices are configured. if not device_list: return self.async_abort(reason='devices_configured') + # Add existing creds for linking. Should be only 1. + if not creds: + # Abort if creds is missing. + return self.async_abort(reason='credential_error') + self.creds = creds['data'] # Login to PS4 with user data. if user_input is not None: From 0ae38aece87591d9ea973feab309c916fd7e6acd Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 24 Mar 2019 20:13:20 -0400 Subject: [PATCH 122/605] Prefer TCP for RTSP streams (#22338) ## Description: For RTSP streams, set the `prefer_tcp` FFMPEG flag. This should resolve some of the "green feed" issues that some users are reporting, likely due to packets being lost over UDP on their network. Resources: [FFMPEG protocols documentation](https://ffmpeg.org/ffmpeg-protocols.html#rtsp) ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/stream/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 3f715af0e047d4..c881ec1276a96f 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -44,6 +44,11 @@ def request_stream(hass, stream_source, *, fmt='hls', if options is None: options = {} + # For RTSP streams, prefer TCP + if isinstance(stream_source, str) \ + and stream_source[:7] == 'rtsp://' and not options: + options['rtsp_flags'] = 'prefer_tcp' + try: streams = hass.data[DOMAIN][ATTR_STREAMS] stream = streams.get(stream_source) From dc64634e21870169bed81dee3bf41e8652264f88 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 24 Mar 2019 17:15:07 -0700 Subject: [PATCH 123/605] Set Onkyo reset log to debug instead of info (#22369) Onkyo logs this message somewhat frequently, and its spammy, so lets make it a debug message instead of info. See also: #20081 --- homeassistant/components/onkyo/media_player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index df30c7e0782789..2fb284bb24a6a3 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -179,7 +179,7 @@ def command(self, command): except (ValueError, OSError, AttributeError, AssertionError): if self._receiver.command_socket: self._receiver.command_socket = None - _LOGGER.info("Resetting connection to %s", self._name) + _LOGGER.debug("Resetting connection to %s", self._name) else: _LOGGER.info("%s is disconnected. Attempting to reconnect", self._name) From d2a83c2732af5d9df221321f73aac4d7a943d5af Mon Sep 17 00:00:00 2001 From: cgtobi Date: Mon, 25 Mar 2019 01:30:21 +0100 Subject: [PATCH 124/605] Upgrade netatmo smart_home module (#22365) --- homeassistant/components/netatmo/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index 2e580627543d38..2036e55b3a88ad 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -12,7 +12,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['pyatmo==1.8'] +REQUIREMENTS = ['pyatmo==1.9'] DEPENDENCIES = ['webhook'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 15ae90ebe0b986..8a65d124f1c638 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -949,7 +949,7 @@ pyalarmdotcom==0.3.2 pyarlo==0.2.3 # homeassistant.components.netatmo -pyatmo==1.8 +pyatmo==1.9 # homeassistant.components.apple_tv pyatv==0.3.12 From 0d46e2c0b5bda6d2d3def81cfe053fa13548d6ed Mon Sep 17 00:00:00 2001 From: shanbs Date: Mon, 25 Mar 2019 01:31:22 +0100 Subject: [PATCH 125/605] Fix the crash due to absence of the "default_home" in HomeData from pyatmo (netatmo/climate) (#22363) --- homeassistant/components/netatmo/climate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index 2d8b06dd466d0d..d0537c5912b18a 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -315,6 +315,8 @@ def setup(self): self.home_id = self.homedata.gethomeId(self.home) except TypeError: _LOGGER.error("Error when getting home data.") + except AttributeError: + _LOGGER.error("No default_home in HomeData.") except pyatmo.NoDevice: _LOGGER.debug("No thermostat devices available.") From 1b0e523a60fd75d3b5b1996b9f40433e6b94a8a8 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Mon, 25 Mar 2019 01:40:27 +0100 Subject: [PATCH 126/605] Add support for 'image' media type (#22353) --- .../components/dlna_dmr/media_player.py | 38 ++++++++++++------- .../components/media_player/const.py | 1 + .../components/media_player/services.yaml | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index aae2e9b6af9cfd..9cf42bfec603f6 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -17,6 +17,9 @@ from homeassistant.components.media_player import ( MediaPlayerDevice, PLATFORM_SCHEMA) from homeassistant.components.media_player.const import ( + MEDIA_TYPE_CHANNEL, MEDIA_TYPE_EPISODE, MEDIA_TYPE_IMAGE, + MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, + MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) @@ -51,20 +54,25 @@ }) HOME_ASSISTANT_UPNP_CLASS_MAPPING = { - 'music': 'object.item.audioItem', - 'tvshow': 'object.item.videoItem', - 'video': 'object.item.videoItem', - 'episode': 'object.item.videoItem', - 'channel': 'object.item.videoItem', - 'playlist': 'object.item.playlist', + MEDIA_TYPE_MUSIC: 'object.item.audioItem', + MEDIA_TYPE_TVSHOW: 'object.item.videoItem', + MEDIA_TYPE_MOVIE: 'object.item.videoItem', + MEDIA_TYPE_VIDEO: 'object.item.videoItem', + MEDIA_TYPE_EPISODE: 'object.item.videoItem', + MEDIA_TYPE_CHANNEL: 'object.item.videoItem', + MEDIA_TYPE_IMAGE: 'object.item.imageItem', + MEDIA_TYPE_PLAYLIST: 'object.item.playlist', } +UPNP_CLASS_DEFAULT = 'object.item' HOME_ASSISTANT_UPNP_MIME_TYPE_MAPPING = { - 'music': 'audio/*', - 'tvshow': 'video/*', - 'video': 'video/*', - 'episode': 'video/*', - 'channel': 'video/*', - 'playlist': 'playlist/*', + MEDIA_TYPE_MUSIC: 'audio/*', + MEDIA_TYPE_TVSHOW: 'video/*', + MEDIA_TYPE_MOVIE: 'video/*', + MEDIA_TYPE_VIDEO: 'video/*', + MEDIA_TYPE_EPISODE: 'video/*', + MEDIA_TYPE_CHANNEL: 'video/*', + MEDIA_TYPE_IMAGE: 'image/*', + MEDIA_TYPE_PLAYLIST: 'playlist/*', } @@ -319,8 +327,10 @@ async def async_media_seek(self, position): async def async_play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" title = "Home Assistant" - mime_type = HOME_ASSISTANT_UPNP_MIME_TYPE_MAPPING[media_type] - upnp_class = HOME_ASSISTANT_UPNP_CLASS_MAPPING[media_type] + mime_type = HOME_ASSISTANT_UPNP_MIME_TYPE_MAPPING.get(media_type, + media_type) + upnp_class = HOME_ASSISTANT_UPNP_CLASS_MAPPING.get(media_type, + UPNP_CLASS_DEFAULT) # Stop current playing media if self._device.can_stop: diff --git a/homeassistant/components/media_player/const.py b/homeassistant/components/media_player/const.py index bf7e6b4e0cef6a..54e28d2d17e3d4 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -36,6 +36,7 @@ MEDIA_TYPE_EPISODE = 'episode' MEDIA_TYPE_CHANNEL = 'channel' MEDIA_TYPE_PLAYLIST = 'playlist' +MEDIA_TYPE_IMAGE = 'image' MEDIA_TYPE_URL = 'url' SERVICE_CLEAR_PLAYLIST = 'clear_playlist' diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 3c91f19469b2b3..c9da38d3657f2c 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -131,7 +131,7 @@ play_media: description: The ID of the content to play. Platform dependent. example: 'https://home-assistant.io/images/cast/splash.png' media_content_type: - description: The type of the content to play. Must be one of music, tvshow, video, episode, channel or playlist + description: The type of the content to play. Must be one of image, music, tvshow, video, episode, channel or playlist example: 'music' select_source: From 7f940423ade4d14568889608134c28805db8d0b5 Mon Sep 17 00:00:00 2001 From: Hmmbob <33529490+hmmbob@users.noreply.github.com> Date: Mon, 25 Mar 2019 01:40:43 +0100 Subject: [PATCH 127/605] Warn user about HTML5 GCM deprecation (#22351) * Warn user about GCM deprecation * Fixing hound * Fixing typo * Fixing Travis fail --- homeassistant/components/notify/html5.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index f9bf36e61f0936..0e99727e81bfb2 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -45,8 +45,23 @@ ATTR_VAPID_PRV_KEY = 'vapid_prv_key' ATTR_VAPID_EMAIL = 'vapid_email' + +def gcm_api_deprecated(value): + """Warn user that GCM API config is deprecated.""" + if not value: + return value + + _LOGGER.warning( + "Configuring html5_push_notifications via the GCM api" + " has been deprecated and will stop working after April 11," + " 2019. Use the VAPID configuration instead. For instructions," + " see https://www.home-assistant.io/components/notify.html5/") + return value + + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(ATTR_GCM_SENDER_ID): cv.string, + vol.Optional(ATTR_GCM_SENDER_ID): + vol.All(cv.string, gcm_api_deprecated), vol.Optional(ATTR_GCM_API_KEY): cv.string, vol.Optional(ATTR_VAPID_PUB_KEY): cv.string, vol.Optional(ATTR_VAPID_PRV_KEY): cv.string, From adca598172403d0233070326a4ffc59be0558af1 Mon Sep 17 00:00:00 2001 From: dilruacs Date: Mon, 25 Mar 2019 01:41:16 +0100 Subject: [PATCH 128/605] Turn Panasonic Viera TV on without WOL (#22084) * Turn the TV on via remote * Turn the TV on via remote * Use turn_on() from panasonic-viera==0.3.2 * make power option configurable * add app_power as argument * formatting --- .../panasonic_viera/media_player.py | 20 ++++++++++++++----- requirements_all.txt | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/panasonic_viera/media_player.py b/homeassistant/components/panasonic_viera/media_player.py index e6546f7c1e2767..f1ac0cd90d461a 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -19,12 +19,15 @@ CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['panasonic_viera==0.3.1', 'wakeonlan==1.1.6'] +REQUIREMENTS = ['panasonic_viera==0.3.2', 'wakeonlan==1.1.6'] _LOGGER = logging.getLogger(__name__) +CONF_APP_POWER = 'app_power' + DEFAULT_NAME = 'Panasonic Viera TV' DEFAULT_PORT = 55000 +DEFAULT_APP_POWER = False SUPPORT_VIERATV = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP | \ SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ @@ -37,6 +40,7 @@ vol.Optional(CONF_MAC): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_APP_POWER, default=DEFAULT_APP_POWER): cv.boolean, }) @@ -47,6 +51,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): mac = config.get(CONF_MAC) name = config.get(CONF_NAME) port = config.get(CONF_PORT) + app_power = config.get(CONF_APP_POWER) if discovery_info: _LOGGER.debug('%s', discovery_info) @@ -59,20 +64,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None): else: uuid = None remote = RemoteControl(host, port) - add_entities([PanasonicVieraTVDevice(mac, name, remote, host, uuid)]) + add_entities([PanasonicVieraTVDevice( + mac, name, remote, host, app_power, uuid)]) return True host = config.get(CONF_HOST) remote = RemoteControl(host, port) - add_entities([PanasonicVieraTVDevice(mac, name, remote, host)]) + add_entities([PanasonicVieraTVDevice(mac, name, remote, host, app_power)]) return True class PanasonicVieraTVDevice(MediaPlayerDevice): """Representation of a Panasonic Viera TV.""" - def __init__(self, mac, name, remote, host, uuid=None): + def __init__(self, mac, name, remote, host, app_power, uuid=None): """Initialize the Panasonic device.""" import wakeonlan # Save a reference to the imported class @@ -86,6 +92,7 @@ def __init__(self, mac, name, remote, host, uuid=None): self._remote = remote self._host = host self._volume = 0 + self._app_power = app_power @property def unique_id(self) -> str: @@ -134,7 +141,7 @@ def is_volume_muted(self): @property def supported_features(self): """Flag media player features that are supported.""" - if self._mac: + if self._mac or self._app_power: return SUPPORT_VIERATV | SUPPORT_TURN_ON return SUPPORT_VIERATV @@ -143,6 +150,9 @@ def turn_on(self): if self._mac: self._wol.send_magic_packet(self._mac, ip_address=self._host) self._state = STATE_ON + elif self._app_power: + self._remote.turn_on() + self._state = STATE_ON def turn_off(self): """Turn off media player.""" diff --git a/requirements_all.txt b/requirements_all.txt index 8a65d124f1c638..1891d2a385ab9e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -802,7 +802,7 @@ paho-mqtt==1.4.0 panacotta==0.1 # homeassistant.components.panasonic_viera.media_player -panasonic_viera==0.3.1 +panasonic_viera==0.3.2 # homeassistant.components.dunehd.media_player pdunehd==1.3 From d1f75fcf320852ecd9e2fddacb79deeb46cb7bc9 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Mon, 25 Mar 2019 01:46:15 +0100 Subject: [PATCH 129/605] Properly connect sensors to hub (#21414) * Properly connect sensors to hub Refs #20958 * Don't connect (merge) with main device * Provide manufacturer * Linting * Do connect upnp-sensors to main device * Linting * Fix requirements_all.txt --- homeassistant/components/upnp/device.py | 5 ++++- homeassistant/components/upnp/sensor.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/upnp/device.py b/homeassistant/components/upnp/device.py index 6bbf0a3dd53758..5ebe2a78d0d585 100644 --- a/homeassistant/components/upnp/device.py +++ b/homeassistant/components/upnp/device.py @@ -23,7 +23,10 @@ def __init__(self, igd_device): async def async_discover(cls, hass: HomeAssistantType): """Discovery UPNP/IGD devices.""" _LOGGER.debug('Discovering UPnP/IGD devices') - local_ip = hass.data[DOMAIN]['config'].get(CONF_LOCAL_IP) + local_ip = None + if DOMAIN in hass.data and \ + 'config' in hass.data[DOMAIN]: + local_ip = hass.data[DOMAIN]['config'].get(CONF_LOCAL_IP) if local_ip: local_ip = IPv4Address(local_ip) diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index 5f544e1a134fb3..708ef314ab4e28 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -8,8 +8,10 @@ import logging from homeassistant.core import callback +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity +from homeassistant.helpers.typing import HomeAssistantType from .const import DOMAIN as DOMAIN_UPNP, SIGNAL_REMOVE_SENSOR @@ -46,7 +48,9 @@ KBYTE = 1024 -async def async_setup_platform(hass, config, async_add_entities, +async def async_setup_platform(hass: HomeAssistantType, + config, + async_add_entities, discovery_info=None): """Old way of setting up UPnP/IGD sensors.""" _LOGGER.debug('async_setup_platform: config: %s, discovery: %s', @@ -111,8 +115,11 @@ def device_info(self): 'identifiers': { (DOMAIN_UPNP, self.unique_id) }, + 'connections': { + (dr.CONNECTION_UPNP, self._device.udn) + }, 'name': self.name, - 'via_hub': (DOMAIN_UPNP, self._device.udn), + 'manufacturer': self._device.manufacturer, } From b6987a1235765e3c201386513129fe40bdcb157b Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 25 Mar 2019 01:57:53 +0100 Subject: [PATCH 130/605] Add support for Tfiac Climate component (#21823) ## Description: Add support for AC-models that follows the Tfiac protocol. Built together with @mellado. **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io#8910 ## Example entry for `configuration.yaml` (if applicable): ```yaml climate: platform: tfiac host: 192.168.10.26 ``` ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [x] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [x] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [x] New dependencies are only imported inside functions that use them ([example][ex-import]). - [x] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [x] New files were added to `.coveragerc`. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 Co-authored-by: Robbie Trencheny --- .coveragerc | 1 + CODEOWNERS | 3 +- homeassistant/components/tfiac/__init__.py | 1 + homeassistant/components/tfiac/climate.py | 185 +++++++++++++++++++++ requirements_all.txt | 3 + 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/tfiac/__init__.py create mode 100644 homeassistant/components/tfiac/climate.py diff --git a/.coveragerc b/.coveragerc index 42e7d84dc099bf..ec6aad90628d2a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -82,6 +82,7 @@ omit = homeassistant/components/proliphix/climate.py homeassistant/components/radiotherm/climate.py homeassistant/components/sensibo/climate.py + homeassistant/components/tfiac/climate.py homeassistant/components/touchline/climate.py homeassistant/components/venstar/climate.py homeassistant/components/zhong_hong/climate.py diff --git a/CODEOWNERS b/CODEOWNERS index e880177380f0e5..717da8b219ef0b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -241,15 +241,16 @@ homeassistant/components/tautulli/sensor.py @ludeeus homeassistant/components/tellduslive/* @fredrike homeassistant/components/template/cover.py @PhracturedBlue homeassistant/components/tesla/* @zabuldon +homeassistant/components/tfiac/* @fredrike @mellado homeassistant/components/thethingsnetwork/* @fabaff homeassistant/components/threshold/binary_sensor.py @fabaff homeassistant/components/tibber/* @danielhiversen homeassistant/components/tile/device_tracker.py @bachya homeassistant/components/time_date/sensor.py @fabaff +homeassistant/components/toon/* @frenck homeassistant/components/tplink/* @rytilahti homeassistant/components/traccar/device_tracker.py @ludeeus homeassistant/components/tradfri/* @ggravlingen -homeassistant/components/toon/* @frenck # U homeassistant/components/uber/sensor.py @robbiet480 diff --git a/homeassistant/components/tfiac/__init__.py b/homeassistant/components/tfiac/__init__.py new file mode 100644 index 00000000000000..bb097a7edd0d6b --- /dev/null +++ b/homeassistant/components/tfiac/__init__.py @@ -0,0 +1 @@ +"""The tfiac component.""" diff --git a/homeassistant/components/tfiac/climate.py b/homeassistant/components/tfiac/climate.py new file mode 100644 index 00000000000000..44fa19098236c8 --- /dev/null +++ b/homeassistant/components/tfiac/climate.py @@ -0,0 +1,185 @@ +"""Climate platform that offers a climate device for the TFIAC protocol.""" +from concurrent import futures +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice +from homeassistant.components.climate.const import ( + STATE_AUTO, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, STATE_HEAT, + SUPPORT_FAN_MODE, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, + SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, TEMP_FAHRENHEIT +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['pytfiac==0.3'] + +SCAN_INTERVAL = timedelta(seconds=60) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, +}) + +_LOGGER = logging.getLogger(__name__) + +MIN_TEMP = 61 +MAX_TEMP = 88 +OPERATION_MAP = { + STATE_HEAT: 'heat', + STATE_AUTO: 'selfFeel', + STATE_DRY: 'dehumi', + STATE_FAN_ONLY: 'fan', + STATE_COOL: 'cool', +} +OPERATION_MAP_REV = { + v: k for k, v in OPERATION_MAP.items()} +FAN_LIST = ['Auto', 'Low', 'Middle', 'High'] +SWING_LIST = [ + 'Off', + 'Vertical', + 'Horizontal', + 'Both', +] + +CURR_TEMP = 'current_temp' +TARGET_TEMP = 'target_temp' +OPERATION_MODE = 'operation' +FAN_MODE = 'fan_mode' +SWING_MODE = 'swing_mode' +ON_MODE = 'is_on' + + +async def async_setup_platform(hass, config, async_add_devices, + discovery_info=None): + """Set up the TFIAC climate device.""" + from pytfiac import Tfiac + + tfiac_client = Tfiac(config[CONF_HOST]) + try: + await tfiac_client.update() + except futures.TimeoutError: + _LOGGER.error("Unable to connect to %s", config[CONF_HOST]) + return + async_add_devices([TfiacClimate(hass, tfiac_client)]) + + +class TfiacClimate(ClimateDevice): + """TFIAC class.""" + + def __init__(self, hass, client): + """Init class.""" + self._client = client + self._available = True + + @property + def available(self): + """Return if the device is available.""" + return self._available + + async def async_update(self): + """Update status via socket polling.""" + try: + await self._client.update() + self._available = True + except futures.TimeoutError: + self._available = False + + @property + def supported_features(self): + """Return the list of supported features.""" + return (SUPPORT_FAN_MODE | SUPPORT_ON_OFF | SUPPORT_OPERATION_MODE + | SUPPORT_SWING_MODE | SUPPORT_TARGET_TEMPERATURE) + + @property + def min_temp(self): + """Return the minimum temperature.""" + return MIN_TEMP + + @property + def max_temp(self): + """Return the maximum temperature.""" + return MAX_TEMP + + @property + def name(self): + """Return the name of the climate device.""" + return self._client.name + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + return self._client.status['target_temp'] + + @property + def temperature_unit(self): + """Return the unit of measurement.""" + return TEMP_FAHRENHEIT + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._client.status['current_temp'] + + @property + def current_operation(self): + """Return current operation ie. heat, cool, idle.""" + operation = self._client.status['operation'] + return OPERATION_MAP_REV.get(operation, operation) + + @property + def is_on(self): + """Return true if on.""" + return self._client.status[ON_MODE] == 'on' + + @property + def operation_list(self): + """Return the list of available operation modes.""" + return sorted(OPERATION_MAP) + + @property + def current_fan_mode(self): + """Return the fan setting.""" + return self._client.status['fan_mode'] + + @property + def fan_list(self): + """Return the list of available fan modes.""" + return FAN_LIST + + @property + def current_swing_mode(self): + """Return the swing setting.""" + return self._client.status['swing_mode'] + + @property + def swing_list(self): + """List of available swing modes.""" + return SWING_LIST + + async def async_set_temperature(self, **kwargs): + """Set new target temperature.""" + if kwargs.get(ATTR_TEMPERATURE) is not None: + await self._client.set_state(TARGET_TEMP, + kwargs.get(ATTR_TEMPERATURE)) + + async def async_set_operation_mode(self, operation_mode): + """Set new operation mode.""" + await self._client.set_state(OPERATION_MODE, + OPERATION_MAP[operation_mode]) + + async def async_set_fan_mode(self, fan_mode): + """Set new fan mode.""" + await self._client.set_state(FAN_MODE, fan_mode) + + async def async_set_swing_mode(self, swing_mode): + """Set new swing mode.""" + await self._client.set_swing(swing_mode) + + async def async_turn_on(self): + """Turn device on.""" + await self._client.set_state(ON_MODE, 'on') + + async def async_turn_off(self): + """Turn device off.""" + await self._client.set_state(ON_MODE, 'off') diff --git a/requirements_all.txt b/requirements_all.txt index 1891d2a385ab9e..80274fdbfd3719 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1299,6 +1299,9 @@ pytautulli==0.5.0 # homeassistant.components.liveboxplaytv.media_player pyteleloisirs==3.4 +# homeassistant.components.tfiac.climate +pytfiac==0.3 + # homeassistant.components.thinkingcleaner.sensor # homeassistant.components.thinkingcleaner.switch pythinkingcleaner==0.0.3 From 1aee7a1673ac43497369f97234d4c07f36b11ddf Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 24 Mar 2019 17:58:20 -0700 Subject: [PATCH 131/605] Add aws component and consolidate aws notify platform (#22240) * Add aws component * Move notify config under aws component * Add basic tests for aws component * Add deprecated warning for notify.aws_* * Add more tests --- homeassistant/components/aws/__init__.py | 147 +++++++++ homeassistant/components/aws/config_flow.py | 22 ++ homeassistant/components/aws/const.py | 13 + homeassistant/components/aws/notify.py | 278 ++++++++++++++++++ homeassistant/components/notify/aws_lambda.py | 6 + homeassistant/components/notify/aws_sns.py | 6 + homeassistant/components/notify/aws_sqs.py | 6 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/aws/__init__.py | 1 + tests/components/aws/test_init.py | 199 +++++++++++++ 12 files changed, 685 insertions(+) create mode 100644 homeassistant/components/aws/__init__.py create mode 100644 homeassistant/components/aws/config_flow.py create mode 100644 homeassistant/components/aws/const.py create mode 100644 homeassistant/components/aws/notify.py create mode 100644 tests/components/aws/__init__.py create mode 100644 tests/components/aws/test_init.py diff --git a/homeassistant/components/aws/__init__.py b/homeassistant/components/aws/__init__.py new file mode 100644 index 00000000000000..bd1f6b550909a8 --- /dev/null +++ b/homeassistant/components/aws/__init__.py @@ -0,0 +1,147 @@ +"""Support for Amazon Web Services (AWS).""" +import asyncio +import logging +from collections import OrderedDict + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import ATTR_CREDENTIALS, CONF_NAME, CONF_PROFILE_NAME +from homeassistant.helpers import config_validation as cv, discovery + +# Loading the config flow file will register the flow +from . import config_flow # noqa +from .const import ( + CONF_ACCESS_KEY_ID, + CONF_SECRET_ACCESS_KEY, + DATA_CONFIG, + DATA_HASS_CONFIG, + DATA_SESSIONS, + DOMAIN, + CONF_NOTIFY, +) +from .notify import PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA + +REQUIREMENTS = ["aiobotocore==0.10.2"] + +_LOGGER = logging.getLogger(__name__) + +AWS_CREDENTIAL_SCHEMA = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, + vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, + vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, + } +) + +DEFAULT_CREDENTIAL = [{CONF_NAME: "default", CONF_PROFILE_NAME: "default"}] + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Optional( + ATTR_CREDENTIALS, default=DEFAULT_CREDENTIAL + ): vol.All(cv.ensure_list, [AWS_CREDENTIAL_SCHEMA]), + vol.Optional(CONF_NOTIFY): vol.All( + cv.ensure_list, [NOTIFY_PLATFORM_SCHEMA] + ), + } + ) + }, + extra=vol.ALLOW_EXTRA, +) + + +async def async_setup(hass, config): + """Set up AWS component.""" + hass.data[DATA_HASS_CONFIG] = config + + conf = config.get(DOMAIN) + if conf is None: + # create a default conf using default profile + conf = CONFIG_SCHEMA({ATTR_CREDENTIALS: DEFAULT_CREDENTIAL}) + + hass.data[DATA_CONFIG] = conf + hass.data[DATA_SESSIONS] = OrderedDict() + + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=conf + ) + ) + + return True + + +async def async_setup_entry(hass, entry): + """Load a config entry. + + Validate and save sessions per aws credential. + """ + config = hass.data.get(DATA_HASS_CONFIG) + conf = hass.data.get(DATA_CONFIG) + + if entry.source == config_entries.SOURCE_IMPORT: + if conf is None: + # user removed config from configuration.yaml, abort setup + hass.async_create_task( + hass.config_entries.async_remove(entry.entry_id) + ) + return False + + if conf != entry.data: + # user changed config from configuration.yaml, use conf to setup + hass.config_entries.async_update_entry(entry, data=conf) + + if conf is None: + conf = CONFIG_SCHEMA({DOMAIN: entry.data})[DOMAIN] + + validation = True + tasks = [] + for cred in conf.get(ATTR_CREDENTIALS): + tasks.append(_validate_aws_credentials(hass, cred)) + if tasks: + results = await asyncio.gather(*tasks, return_exceptions=True) + for index, result in enumerate(results): + name = conf[ATTR_CREDENTIALS][index][CONF_NAME] + if isinstance(result, Exception): + _LOGGER.error( + "Validating credential [%s] failed: %s", + name, result, exc_info=result + ) + validation = False + else: + hass.data[DATA_SESSIONS][name] = result + + # No entry support for notify component yet + for notify_config in conf.get(CONF_NOTIFY, []): + discovery.load_platform(hass, "notify", DOMAIN, notify_config, config) + + return validation + + +async def _validate_aws_credentials(hass, credential): + """Validate AWS credential config.""" + import aiobotocore + + aws_config = credential.copy() + del aws_config[CONF_NAME] + + profile = aws_config.get(CONF_PROFILE_NAME) + + if profile is not None: + session = aiobotocore.AioSession(profile=profile, loop=hass.loop) + del aws_config[CONF_PROFILE_NAME] + if CONF_ACCESS_KEY_ID in aws_config: + del aws_config[CONF_ACCESS_KEY_ID] + if CONF_SECRET_ACCESS_KEY in aws_config: + del aws_config[CONF_SECRET_ACCESS_KEY] + else: + session = aiobotocore.AioSession(loop=hass.loop) + + async with session.create_client("iam", **aws_config) as client: + await client.get_user() + + return session diff --git a/homeassistant/components/aws/config_flow.py b/homeassistant/components/aws/config_flow.py new file mode 100644 index 00000000000000..c21f2a94137f6a --- /dev/null +++ b/homeassistant/components/aws/config_flow.py @@ -0,0 +1,22 @@ +"""Config flow for AWS component.""" + +from homeassistant import config_entries + +from .const import DOMAIN + + +@config_entries.HANDLERS.register(DOMAIN) +class AWSFlowHandler(config_entries.ConfigFlow): + """Handle a config flow.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH + + async def async_step_import(self, user_input): + """Import a config entry.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + + return self.async_create_entry( + title="configuration.yaml", data=user_input + ) diff --git a/homeassistant/components/aws/const.py b/homeassistant/components/aws/const.py new file mode 100644 index 00000000000000..c8b0eed8b6bbe3 --- /dev/null +++ b/homeassistant/components/aws/const.py @@ -0,0 +1,13 @@ +"""Constant for AWS component.""" +DOMAIN = "aws" +DATA_KEY = DOMAIN +DATA_CONFIG = "aws_config" +DATA_HASS_CONFIG = "aws_hass_config" +DATA_SESSIONS = "aws_sessions" + +CONF_REGION = "region_name" +CONF_ACCESS_KEY_ID = "aws_access_key_id" +CONF_SECRET_ACCESS_KEY = "aws_secret_access_key" +CONF_PROFILE_NAME = "profile_name" +CONF_CREDENTIAL_NAME = "credential_name" +CONF_NOTIFY = "notify" diff --git a/homeassistant/components/aws/notify.py b/homeassistant/components/aws/notify.py new file mode 100644 index 00000000000000..020d92200b98ef --- /dev/null +++ b/homeassistant/components/aws/notify.py @@ -0,0 +1,278 @@ +"""AWS platform for notify component.""" +import asyncio +import logging +import json +import base64 + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_PLATFORM, CONF_NAME, ATTR_CREDENTIALS +from homeassistant.components.notify import ( + ATTR_TARGET, + ATTR_TITLE, + ATTR_TITLE_DEFAULT, + BaseNotificationService, + PLATFORM_SCHEMA, +) +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.json import JSONEncoder + +from .const import ( + CONF_ACCESS_KEY_ID, + CONF_CREDENTIAL_NAME, + CONF_PROFILE_NAME, + CONF_REGION, + CONF_SECRET_ACCESS_KEY, + DATA_SESSIONS, +) + +DEPENDENCIES = ["aws"] + +_LOGGER = logging.getLogger(__name__) + +CONF_CONTEXT = "context" +CONF_SERVICE = "service" + +SUPPORTED_SERVICES = ["lambda", "sns", "sqs"] + + +def _in_avilable_region(config): + """Check if region is available.""" + import aiobotocore + + session = aiobotocore.get_session() + available_regions = session.get_available_regions(config[CONF_SERVICE]) + if config[CONF_REGION] not in available_regions: + raise vol.Invalid( + "Region {} is not available for {} service, mustin {}".format( + config[CONF_REGION], config[CONF_SERVICE], available_regions + ) + ) + return config + + +PLATFORM_SCHEMA = vol.Schema( + vol.All( + PLATFORM_SCHEMA.extend( + { + # override notify.PLATFORM_SCHEMA.CONF_PLATFORM to Optional + # we don't need this field when we use discovery + vol.Optional(CONF_PLATFORM): cv.string, + vol.Required(CONF_SERVICE): vol.All( + cv.string, vol.Lower, vol.In(SUPPORTED_SERVICES) + ), + vol.Required(CONF_REGION): vol.All(cv.string, vol.Lower), + vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, + vol.Inclusive( + CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS + ): cv.string, + vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, + vol.Exclusive( + CONF_CREDENTIAL_NAME, ATTR_CREDENTIALS + ): cv.string, + vol.Optional(CONF_CONTEXT): vol.Coerce(dict), + }, + extra=vol.PREVENT_EXTRA, + ), + _in_avilable_region, + ) +) + + +async def async_get_service(hass, config, discovery_info=None): + """Get the AWS notification service.""" + import aiobotocore + + session = None + + if discovery_info is not None: + conf = discovery_info + else: + conf = config + + service = conf[CONF_SERVICE] + region_name = conf[CONF_REGION] + + aws_config = conf.copy() + + del aws_config[CONF_SERVICE] + del aws_config[CONF_REGION] + if CONF_PLATFORM in aws_config: + del aws_config[CONF_PLATFORM] + if CONF_NAME in aws_config: + del aws_config[CONF_NAME] + if CONF_CONTEXT in aws_config: + del aws_config[CONF_CONTEXT] + + if not aws_config: + # no platform config, use aws component config instead + if hass.data[DATA_SESSIONS]: + session = list(hass.data[DATA_SESSIONS].values())[0] + else: + raise ValueError( + "No available aws session for {}".format(config[CONF_NAME]) + ) + + if session is None: + credential_name = aws_config.get(CONF_CREDENTIAL_NAME) + if credential_name is not None: + session = hass.data[DATA_SESSIONS].get(credential_name) + if session is None: + _LOGGER.warning( + "No available aws session for %s", credential_name + ) + del aws_config[CONF_CREDENTIAL_NAME] + + if session is None: + profile = aws_config.get(CONF_PROFILE_NAME) + if profile is not None: + session = aiobotocore.AioSession(profile=profile, loop=hass.loop) + del aws_config[CONF_PROFILE_NAME] + else: + session = aiobotocore.AioSession(loop=hass.loop) + + aws_config[CONF_REGION] = region_name + + if service == "lambda": + context_str = json.dumps( + {"custom": conf.get(CONF_CONTEXT, {})}, cls=JSONEncoder + ) + context_b64 = base64.b64encode(context_str.encode("utf-8")) + context = context_b64.decode("utf-8") + return AWSLambda(session, aws_config, context) + + if service == "sns": + return AWSSNS(session, aws_config) + + if service == "sqs": + return AWSSQS(session, aws_config) + + raise ValueError("Unsupported service {}".format(service)) + + +class AWSNotify(BaseNotificationService): + """Implement the notification service for the AWS service.""" + + def __init__(self, session, aws_config): + """Initialize the service.""" + self.session = session + self.aws_config = aws_config + + def send_message(self, message, **kwargs): + """Send notification.""" + raise NotImplementedError("Please call async_send_message()") + + async def async_send_message(self, message="", **kwargs): + """Send notification.""" + targets = kwargs.get(ATTR_TARGET) + + if not targets: + raise HomeAssistantError("At least one target is required") + + +class AWSLambda(AWSNotify): + """Implement the notification service for the AWS Lambda service.""" + + service = "lambda" + + def __init__(self, session, aws_config, context): + """Initialize the service.""" + super().__init__(session, aws_config) + self.context = context + + async def async_send_message(self, message="", **kwargs): + """Send notification to specified LAMBDA ARN.""" + await super().async_send_message(message, **kwargs) + + cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) + payload = {"message": message} + payload.update(cleaned_kwargs) + json_payload = json.dumps(payload) + + async with self.session.create_client( + self.service, **self.aws_config + ) as client: + tasks = [] + for target in kwargs.get(ATTR_TARGET, []): + tasks.append( + client.invoke( + FunctionName=target, + Payload=json_payload, + ClientContext=self.context, + ) + ) + + if tasks: + await asyncio.gather(*tasks) + + +class AWSSNS(AWSNotify): + """Implement the notification service for the AWS SNS service.""" + + service = "sns" + + async def async_send_message(self, message="", **kwargs): + """Send notification to specified SNS ARN.""" + await super().async_send_message(message, **kwargs) + + message_attributes = { + k: {"StringValue": json.dumps(v), "DataType": "String"} + for k, v in kwargs.items() + if v + } + subject = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) + + async with self.session.create_client( + self.service, **self.aws_config + ) as client: + tasks = [] + for target in kwargs.get(ATTR_TARGET, []): + tasks.append( + client.publish( + TargetArn=target, + Message=message, + Subject=subject, + MessageAttributes=message_attributes, + ) + ) + + if tasks: + await asyncio.gather(*tasks) + + +class AWSSQS(AWSNotify): + """Implement the notification service for the AWS SQS service.""" + + service = "sqs" + + async def async_send_message(self, message="", **kwargs): + """Send notification to specified SQS ARN.""" + await super().async_send_message(message, **kwargs) + + cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) + message_body = {"message": message} + message_body.update(cleaned_kwargs) + json_body = json.dumps(message_body) + message_attributes = {} + for key, val in cleaned_kwargs.items(): + message_attributes[key] = { + "StringValue": json.dumps(val), + "DataType": "String", + } + + async with self.session.create_client( + self.service, **self.aws_config + ) as client: + tasks = [] + for target in kwargs.get(ATTR_TARGET, []): + tasks.append( + client.send_message( + QueueUrl=target, + MessageBody=json_body, + MessageAttributes=message_attributes, + ) + ) + + if tasks: + await asyncio.gather(*tasks) diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index e605f82c3f15aa..8f639a653c3bf8 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -38,6 +38,12 @@ def get_service(hass, config, discovery_info=None): """Get the AWS Lambda notification service.""" + _LOGGER.warning( + "aws_lambda notify platform is deprecated, please replace it" + " with aws component. This config will become invalid in version 0.92." + " See https://www.home-assistant.io/components/aws/ for details." + ) + context_str = json.dumps({'custom': config[CONF_CONTEXT]}, cls=JSONEncoder) context_b64 = base64.b64encode(context_str.encode('utf-8')) context = context_b64.decode('utf-8') diff --git a/homeassistant/components/notify/aws_sns.py b/homeassistant/components/notify/aws_sns.py index 9363576fc1aced..7fa0e25b32a21a 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/notify/aws_sns.py @@ -35,6 +35,12 @@ def get_service(hass, config, discovery_info=None): """Get the AWS SNS notification service.""" + _LOGGER.warning( + "aws_sns notify platform is deprecated, please replace it" + " with aws component. This config will become invalid in version 0.92." + " See https://www.home-assistant.io/components/aws/ for details." + ) + import boto3 aws_config = config.copy() diff --git a/homeassistant/components/notify/aws_sqs.py b/homeassistant/components/notify/aws_sqs.py index ed22147cfedc3a..927824299398b3 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/notify/aws_sqs.py @@ -33,6 +33,12 @@ def get_service(hass, config, discovery_info=None): """Get the AWS SQS notification service.""" + _LOGGER.warning( + "aws_sqs notify platform is deprecated, please replace it" + " with aws component. This config will become invalid in version 0.92." + " See https://www.home-assistant.io/components/aws/ for details." + ) + import boto3 aws_config = config.copy() diff --git a/requirements_all.txt b/requirements_all.txt index 80274fdbfd3719..de994fa9122131 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -105,6 +105,9 @@ aioasuswrt==1.1.21 # homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 +# homeassistant.components.aws +aiobotocore==0.10.2 + # homeassistant.components.dnsip.sensor aiodns==1.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 05a14e18fc08c1..fe026a3813cfec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -40,6 +40,9 @@ aioambient==0.1.3 # homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 +# homeassistant.components.aws +aiobotocore==0.10.2 + # homeassistant.components.emulated_hue # homeassistant.components.http aiohttp_cors==0.7.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 3c605ef7ae33b6..5d21681aedace1 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -41,6 +41,7 @@ TEST_REQUIREMENTS = ( 'aioambient', 'aioautomatic', + 'aiobotocore', 'aiohttp_cors', 'aiohue', 'aiounifi', diff --git a/tests/components/aws/__init__.py b/tests/components/aws/__init__.py new file mode 100644 index 00000000000000..270922b1e1ed9e --- /dev/null +++ b/tests/components/aws/__init__.py @@ -0,0 +1 @@ +"""Tests for the aws component.""" diff --git a/tests/components/aws/test_init.py b/tests/components/aws/test_init.py new file mode 100644 index 00000000000000..89dd9deaa0ab8d --- /dev/null +++ b/tests/components/aws/test_init.py @@ -0,0 +1,199 @@ +"""Tests for the aws component config and setup.""" +from asynctest import patch as async_patch, MagicMock, CoroutineMock + +from homeassistant.components import aws +from homeassistant.setup import async_setup_component + + +class MockAioSession: + """Mock AioSession.""" + + def __init__(self, *args, **kwargs): + """Init a mock session.""" + + def create_client(self, *args, **kwargs): # pylint: disable=no-self-use + """Create a mocked client.""" + return MagicMock( + __aenter__=CoroutineMock(return_value=CoroutineMock( + get_user=CoroutineMock(), # iam + invoke=CoroutineMock(), # lambda + publish=CoroutineMock(), # sns + send_message=CoroutineMock(), # sqs + )), + __aexit__=CoroutineMock() + ) + + +async def test_empty_config(hass): + """Test a default config will be create for empty config.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': {} + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + assert isinstance(sessions.get('default'), MockAioSession) + + +async def test_empty_credential(hass): + """Test a default config will be create for empty credential section.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'notify': [{ + 'service': 'lambda', + 'name': 'New Lambda Test', + 'region_name': 'us-east-1', + }] + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + assert isinstance(sessions.get('default'), MockAioSession) + + assert hass.services.has_service('notify', 'new_lambda_test') is True + await hass.services.async_call( + 'notify', + 'new_lambda_test', + {'message': 'test', 'target': 'ARN'}, + blocking=True + ) + + +async def test_profile_credential(hass): + """Test credentials with profile name.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'credentials': { + 'name': 'test', + 'profile_name': 'test-profile', + }, + 'notify': [{ + 'service': 'sns', + 'credential_name': 'test', + 'name': 'SNS Test', + 'region_name': 'us-east-1', + }] + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + assert isinstance(sessions.get('test'), MockAioSession) + + assert hass.services.has_service('notify', 'sns_test') is True + await hass.services.async_call( + 'notify', + 'sns_test', + {'title': 'test', 'message': 'test', 'target': 'ARN'}, + blocking=True + ) + + +async def test_access_key_credential(hass): + """Test credentials with access key.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'credentials': [ + { + 'name': 'test', + 'profile_name': 'test-profile', + }, + { + 'name': 'key', + 'aws_access_key_id': 'test-key', + 'aws_secret_access_key': 'test-secret', + }, + ], + 'notify': [{ + 'service': 'sns', + 'credential_name': 'key', + 'name': 'SNS Test', + 'region_name': 'us-east-1', + }] + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 2 + assert isinstance(sessions.get('key'), MockAioSession) + + assert hass.services.has_service('notify', 'sns_test') is True + await hass.services.async_call( + 'notify', + 'sns_test', + {'title': 'test', 'message': 'test', 'target': 'ARN'}, + blocking=True + ) + + +async def test_notify_credential(hass): + """Test notify service can use access key directly.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'notify': [{ + 'service': 'sqs', + 'credential_name': 'test', + 'name': 'SQS Test', + 'region_name': 'us-east-1', + 'aws_access_key_id': 'some-key', + 'aws_secret_access_key': 'some-secret', + }] + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + assert isinstance(sessions.get('default'), MockAioSession) + + assert hass.services.has_service('notify', 'sqs_test') is True + await hass.services.async_call( + 'notify', + 'sqs_test', + {'message': 'test', 'target': 'ARN'}, + blocking=True + ) + + +async def test_notify_credential_profile(hass): + """Test notify service can use profile directly.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'notify': [{ + 'service': 'sqs', + 'name': 'SQS Test', + 'region_name': 'us-east-1', + 'profile_name': 'test', + }] + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + assert isinstance(sessions.get('default'), MockAioSession) + + assert hass.services.has_service('notify', 'sqs_test') is True + await hass.services.async_call( + 'notify', + 'sqs_test', + {'message': 'test', 'target': 'ARN'}, + blocking=True + ) From 548371e94c4c47634bfb187878e438190945dc53 Mon Sep 17 00:00:00 2001 From: karlkar Date: Mon, 25 Mar 2019 01:59:12 +0100 Subject: [PATCH 132/605] Check if mac is set when more than 2 gateways (#21834) * Check if mac is set when more than 2 gateways When more than 2 gateways mac is required for each of them. Now voluptuous will require it. * fix line length * remove trailing whitespace * Make it more readable --- .../components/xiaomi_aqara/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index 66fc1fa13dda1e..e98655f9d76de4 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -61,8 +61,7 @@ }) -GATEWAY_CONFIG = vol.Schema({ - vol.Optional(CONF_MAC, default=None): vol.Any(GW_MAC, None), +GATEWAY_CONFIG_MAC_OPT = vol.Schema({ vol.Optional(CONF_KEY): vol.All(cv.string, vol.Length(min=16, max=16)), vol.Optional(CONF_HOST): cv.string, @@ -70,6 +69,14 @@ vol.Optional(CONF_DISABLE, default=False): cv.boolean, }) +GATEWAY_CONFIG_MAC_REQ = vol.Schema({ + vol.Required(CONF_KEY): + vol.All(cv.string, vol.Length(min=16, max=16)), + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_PORT, default=9898): cv.port, + vol.Optional(CONF_DISABLE, default=False): cv.boolean, +}) + def _fix_conf_defaults(config): """Update some configuration defaults.""" @@ -89,7 +96,10 @@ def _fix_conf_defaults(config): CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_GATEWAYS, default={}): - vol.All(cv.ensure_list, [GATEWAY_CONFIG], [_fix_conf_defaults]), + vol.All(cv.ensure_list, vol.Any( + vol.All([GATEWAY_CONFIG_MAC_OPT], vol.Length(max=1)), + vol.All([GATEWAY_CONFIG_MAC_REQ], vol.Length(min=2)) + ), [_fix_conf_defaults]), vol.Optional(CONF_INTERFACE, default='any'): cv.string, vol.Optional(CONF_DISCOVERY_RETRY, default=3): cv.positive_int }) From f272ed3b9142abcb29405988b9446def31443cb7 Mon Sep 17 00:00:00 2001 From: Moritz Fey Date: Mon, 25 Mar 2019 02:10:49 +0100 Subject: [PATCH 133/605] Add 'method' parameter to forgiving_round method (#21708) * Add 'method' parameter to forgiving_round method Fixes #21707 * fix rounding behavior in round() filter * add test cases for new rounding behaviour --- homeassistant/helpers/template.py | 12 ++++++++++-- tests/helpers/test_template.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index d79c68ffd5e45f..24275c87061703 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -442,10 +442,18 @@ def _resolve_state(self, entity_id_or_state): return None -def forgiving_round(value, precision=0): +def forgiving_round(value, precision=0, method="common"): """Round accepted strings.""" try: - value = round(float(value), precision) + # support rounding methods like jinja + multiplier = float(10 ** precision) + if method == "ceil": + value = math.ceil(float(value) * multiplier) / multiplier + elif method == "floor": + value = math.floor(float(value) * multiplier) / multiplier + else: + # if method is common or something else, use common rounding + value = round(float(value), precision) return int(value) if precision == 0 else value except (ValueError, TypeError): # If value can't be converted to float diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 73fe36af26d46a..e0aeb09976d1b8 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -97,6 +97,16 @@ def test_rounding_value(self): '{{ states.sensor.temperature.state | multiply(10) | round }}', self.hass).render() + assert '12.7' == \ + template.Template( + '{{ states.sensor.temperature.state | round(1, "floor") }}', + self.hass).render() + + assert '12.8' == \ + template.Template( + '{{ states.sensor.temperature.state | round(1, "ceil") }}', + self.hass).render() + def test_rounding_value_get_original_value_on_error(self): """Test rounding value get original value on error.""" assert 'None' == \ From c59d45caa35afd7a69165c6c7563dd4ecbfb8417 Mon Sep 17 00:00:00 2001 From: Nick Horvath Date: Sun, 24 Mar 2019 21:13:06 -0400 Subject: [PATCH 134/605] Expose detailed Ecobee equipment status (#20767) * ecobee: expose detailed equipment status * Fix #18244 for ecobee by moving current_operation property to current_operation_mode which is more accurate and defining current_operation properly, thanks @ZetaPhoenix * fix docstring and lint issue * Revert "fix docstring and lint issue" This reverts commit d3a645f075f8a4017756f5eae05f00f05e2556cf. * Revert "Fix #18244 for ecobee by moving current_operation property to current_operation_mode which is more accurate and defining current_operation properly, thanks @ZetaPhoenix" This reverts commit bfd90551ef759b2c160d69da0778df89251e156b. --- homeassistant/components/ecobee/climate.py | 1 + tests/components/ecobee/test_climate.py | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/ecobee/climate.py b/homeassistant/components/ecobee/climate.py index bfc67e7cfafc2d..44a3800afa958f 100644 --- a/homeassistant/components/ecobee/climate.py +++ b/homeassistant/components/ecobee/climate.py @@ -279,6 +279,7 @@ def device_state_attributes(self): "fan": self.fan, "climate_mode": self.mode, "operation": operation, + "equipment_running": status, "climate_list": self.climate_list, "fan_min_on_time": self.fan_min_on_time } diff --git a/tests/components/ecobee/test_climate.py b/tests/components/ecobee/test_climate.py index f4412b5d564e33..3215a9d5b4c68c 100644 --- a/tests/components/ecobee/test_climate.py +++ b/tests/components/ecobee/test_climate.py @@ -183,7 +183,8 @@ def test_device_state_attributes(self): 'fan': 'off', 'fan_min_on_time': 10, 'climate_mode': 'Climate1', - 'operation': 'heat'} == \ + 'operation': 'heat', + 'equipment_running': 'heatPump2'} == \ self.thermostat.device_state_attributes self.ecobee['equipmentStatus'] = 'auxHeat2' @@ -192,7 +193,8 @@ def test_device_state_attributes(self): 'fan': 'off', 'fan_min_on_time': 10, 'climate_mode': 'Climate1', - 'operation': 'heat'} == \ + 'operation': 'heat', + 'equipment_running': 'auxHeat2'} == \ self.thermostat.device_state_attributes self.ecobee['equipmentStatus'] = 'compCool1' assert {'actual_humidity': 15, @@ -200,7 +202,8 @@ def test_device_state_attributes(self): 'fan': 'off', 'fan_min_on_time': 10, 'climate_mode': 'Climate1', - 'operation': 'cool'} == \ + 'operation': 'cool', + 'equipment_running': 'compCool1'} == \ self.thermostat.device_state_attributes self.ecobee['equipmentStatus'] = '' assert {'actual_humidity': 15, @@ -208,7 +211,8 @@ def test_device_state_attributes(self): 'fan': 'off', 'fan_min_on_time': 10, 'climate_mode': 'Climate1', - 'operation': 'idle'} == \ + 'operation': 'idle', + 'equipment_running': ''} == \ self.thermostat.device_state_attributes self.ecobee['equipmentStatus'] = 'Unknown' @@ -217,7 +221,8 @@ def test_device_state_attributes(self): 'fan': 'off', 'fan_min_on_time': 10, 'climate_mode': 'Climate1', - 'operation': 'Unknown'} == \ + 'operation': 'Unknown', + 'equipment_running': 'Unknown'} == \ self.thermostat.device_state_attributes def test_is_away_mode_on(self): From 324a7c7875d0d1bb79df16205526886d8a1382db Mon Sep 17 00:00:00 2001 From: fabtesta Date: Mon, 25 Mar 2019 02:13:56 +0100 Subject: [PATCH 135/605] Add ClickSend "caller" option (#20780) * removed "from" parameter from ClickSend API call Removed "from" parameter from API call to let ClickSend choose a random number and be more compliant with some cellular networks that do not allow incoming calls from the same recipient number. * fixed "line too long (94 > 79 characters)" houndci code review fixed "line too long (94 > 79 characters)" houndci code review * Added new component optional parameter "caller". If defined is used to override default logic that uses recipient phone number in "from" API call parameter * Removed default value for CALLER parameter. If not defined then will take the RECIPIENT value. --- homeassistant/components/notify/clicksend_tts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/notify/clicksend_tts.py b/homeassistant/components/notify/clicksend_tts.py index 1481fea1783cdd..2a7730c4a27c7c 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/notify/clicksend_tts.py @@ -27,6 +27,7 @@ CONF_LANGUAGE = 'language' CONF_VOICE = 'voice' +CONF_CALLER = 'caller' DEFAULT_LANGUAGE = 'en-us' DEFAULT_VOICE = 'female' @@ -38,6 +39,7 @@ vol.Required(CONF_RECIPIENT): cv.string, vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): cv.string, vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): cv.string, + vol.Optional(CONF_CALLER): cv.string, }) @@ -60,10 +62,13 @@ def __init__(self, config): self.recipient = config.get(CONF_RECIPIENT) self.language = config.get(CONF_LANGUAGE) self.voice = config.get(CONF_VOICE) + self.caller = config.get(CONF_CALLER) + if self.caller is None: + self.caller = self.recipient def send_message(self, message="", **kwargs): """Send a voice call to a user.""" - data = ({'messages': [{'source': 'hass.notify', 'from': self.recipient, + data = ({'messages': [{'source': 'hass.notify', 'from': self.caller, 'to': self.recipient, 'body': message, 'lang': self.language, 'voice': self.voice}]}) api_url = "{}/voice/send".format(BASE_API_URL) From af4b85d39d22b07930f1572520797cd551b26695 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Mon, 25 Mar 2019 01:21:04 +0000 Subject: [PATCH 136/605] Give HomeKit locks better names by default (#22333) ## Description: This is a follow up to #22171. There we set the name of an entity based on the `accessory-information` homekit service, rather than using the zeroconf/avahi name metadata. Unfortunately Lock also sets its name from zeroconf directly, rather than picking it up from the base class. This test updates it to be like the other homekit entities and use the base class. (This is from my ongoing homekit_controller configentry branch). ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/homekit_controller/lock.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index a2aac5767bdf61..b084d7525d3b48 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -41,7 +41,6 @@ def __init__(self, accessory, discovery_info): """Initialise the Lock.""" super().__init__(accessory, discovery_info) self._state = None - self._name = discovery_info['model'] self._battery_level = None def get_characteristic_types(self): @@ -60,11 +59,6 @@ def _update_lock_mechanism_current_state(self, value): def _update_battery_level(self, value): self._battery_level = value - @property - def name(self): - """Return the name of this device.""" - return self._name - @property def is_locked(self): """Return true if device is locked.""" From 96133f5e6bf19a8f6585aba07f89e8005937a61e Mon Sep 17 00:00:00 2001 From: zewelor Date: Mon, 25 Mar 2019 08:50:47 +0100 Subject: [PATCH 137/605] Improve yeelight component (#22347) --- homeassistant/components/yeelight/__init__.py | 90 ++----------------- homeassistant/components/yeelight/light.py | 85 ++++++++++++++++-- 2 files changed, 88 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 32e3c5f69e3b6f..ed4e704a6a51be 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -1,23 +1,18 @@ -""" -Support for Xiaomi Yeelight Wifi color bulb. +"""Support for Xiaomi Yeelight WiFi color bulb.""" -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/yeelight/ -""" import logging from datetime import timedelta import voluptuous as vol from homeassistant.components.discovery import SERVICE_YEELIGHT from homeassistant.const import CONF_DEVICES, CONF_NAME, CONF_SCAN_INTERVAL, \ - CONF_HOST, ATTR_ENTITY_ID, CONF_LIGHTS + CONF_HOST, ATTR_ENTITY_ID from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.helpers import discovery from homeassistant.helpers.discovery import load_platform import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import dispatcher_send -from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.service import extract_entity_ids +from homeassistant.helpers.event import track_time_interval REQUIREMENTS = ['yeelight==0.4.3'] @@ -37,7 +32,6 @@ CONF_FLOW_PARAMS = 'flow_params' CONF_CUSTOM_EFFECTS = 'custom_effects' -ATTR_MODE = 'mode' ATTR_COUNT = 'count' ATTR_ACTION = 'action' ATTR_TRANSITIONS = 'transitions' @@ -56,9 +50,6 @@ YEELIGHT_TEMPERATURE_TRANSACTION = 'TemperatureTransition' YEELIGHT_SLEEP_TRANSACTION = 'SleepTransition' -SERVICE_SET_MODE = 'set_mode' -SERVICE_START_FLOW = 'start_flow' - YEELIGHT_FLOW_TRANSITION_SCHEMA = { vol.Optional(ATTR_COUNT, default=0): cv.positive_int, vol.Optional(ATTR_ACTION, default=ACTION_RECOVER): @@ -152,13 +143,8 @@ def _parse_custom_effects(effects_config): def setup(hass, config): """Set up the Yeelight bulbs.""" - from yeelight.enums import PowerMode - conf = config[DOMAIN] - yeelight_data = hass.data[DATA_YEELIGHT] = { - CONF_DEVICES: {}, - CONF_LIGHTS: {}, - } + yeelight_data = hass.data[DATA_YEELIGHT] = {} def device_discovered(service, info): _LOGGER.debug("Adding autodetected %s", info['hostname']) @@ -177,47 +163,14 @@ def device_discovered(service, info): discovery.listen(hass, SERVICE_YEELIGHT, device_discovered) - def async_update(event): - for device in yeelight_data[CONF_DEVICES].values(): + def update(event): + for device in yeelight_data.values(): device.update() - async_track_time_interval( - hass, async_update, conf[CONF_SCAN_INTERVAL] + track_time_interval( + hass, update, conf[CONF_SCAN_INTERVAL] ) - def service_handler(service): - """Dispatch service calls to target entities.""" - params = {key: value for key, value in service.data.items() - if key != ATTR_ENTITY_ID} - - entity_ids = extract_entity_ids(hass, service) - target_devices = [dev.device for dev in - yeelight_data[CONF_LIGHTS].values() - if dev.entity_id in entity_ids] - - for target_device in target_devices: - if service.service == SERVICE_SET_MODE: - target_device.set_mode(**params) - elif service.service == SERVICE_START_FLOW: - params[ATTR_TRANSITIONS] = \ - _transitions_config_parser(params[ATTR_TRANSITIONS]) - target_device.start_flow(**params) - - service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({ - vol.Required(ATTR_MODE): - vol.In([mode.name.lower() for mode in PowerMode]) - }) - hass.services.register( - DOMAIN, SERVICE_SET_MODE, service_handler, - schema=service_schema_set_mode) - - service_schema_start_flow = YEELIGHT_SERVICE_SCHEMA.extend( - YEELIGHT_FLOW_TRANSITION_SCHEMA - ) - hass.services.register( - DOMAIN, SERVICE_START_FLOW, service_handler, - schema=service_schema_start_flow) - for ipaddr, device_config in conf[CONF_DEVICES].items(): _LOGGER.debug("Adding configured %s", device_config[CONF_NAME]) _setup_device(hass, config, ipaddr, device_config) @@ -226,7 +179,7 @@ def service_handler(service): def _setup_device(hass, hass_config, ipaddr, device_config): - devices = hass.data[DATA_YEELIGHT][CONF_DEVICES] + devices = hass.data[DATA_YEELIGHT] if ipaddr in devices: return @@ -330,28 +283,3 @@ def update(self): self._update_properties() dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) - - def set_mode(self, mode: str): - """Set a power mode.""" - import yeelight - - try: - self.bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to set the power mode: %s", ex) - - self.update() - - def start_flow(self, transitions, count=0, action=ACTION_RECOVER): - """Start flow.""" - import yeelight - - try: - flow = yeelight.Flow( - count=count, - action=yeelight.Flow.actions[action], - transitions=transitions) - - self.bulb.start_flow(flow) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to set effect: %s", ex) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 8c7a94d3020659..d208d1f72b0a08 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -1,11 +1,13 @@ """Light platform support for yeelight.""" import logging +import voluptuous as vol from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.service import extract_entity_ids from homeassistant.util.color import ( color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) -from homeassistant.const import CONF_HOST, CONF_DEVICES, CONF_LIGHTS +from homeassistant.const import CONF_HOST, ATTR_ENTITY_ID from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_COLOR_TEMP, @@ -15,7 +17,10 @@ import homeassistant.util.color as color_util from homeassistant.components.yeelight import ( CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, - CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED) + CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED, + YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS, + YEELIGHT_FLOW_TRANSITION_SCHEMA, _transitions_config_parser, + ACTION_RECOVER) DEPENDENCIES = ['yeelight'] @@ -33,6 +38,11 @@ SUPPORT_EFFECT | SUPPORT_COLOR_TEMP) +ATTR_MODE = 'mode' + +SERVICE_SET_MODE = 'set_mode' +SERVICE_START_FLOW = 'start_flow' + EFFECT_DISCO = "Disco" EFFECT_TEMP = "Slow Temp" EFFECT_STROBE = "Strobe epilepsy!" @@ -86,20 +96,57 @@ def _wrap(self, *args, **kwargs): def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yeelight bulbs.""" + from yeelight.enums import PowerMode + + data_key = '{}_lights'.format(DATA_YEELIGHT) + if not discovery_info: return - yeelight_data = hass.data[DATA_YEELIGHT] - ipaddr = discovery_info[CONF_HOST] - device = yeelight_data[CONF_DEVICES][ipaddr] + if data_key not in hass.data: + hass.data[data_key] = [] + + device = hass.data[DATA_YEELIGHT][discovery_info[CONF_HOST]] _LOGGER.debug("Adding %s", device.name) custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] light = YeelightLight(device, custom_effects=custom_effects) - yeelight_data[CONF_LIGHTS][ipaddr] = light + hass.data[data_key].append(light) add_entities([light], True) + def service_handler(service): + """Dispatch service calls to target entities.""" + params = {key: value for key, value in service.data.items() + if key != ATTR_ENTITY_ID} + + entity_ids = extract_entity_ids(hass, service) + target_devices = [light for light in hass.data[data_key] + if light.entity_id in entity_ids] + + for target_device in target_devices: + if service.service == SERVICE_SET_MODE: + target_device.set_mode(**params) + elif service.service == SERVICE_START_FLOW: + params[ATTR_TRANSITIONS] = \ + _transitions_config_parser(params[ATTR_TRANSITIONS]) + target_device.start_flow(**params) + + service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_MODE): + vol.In([mode.name.lower() for mode in PowerMode]) + }) + hass.services.register( + DOMAIN, SERVICE_SET_MODE, service_handler, + schema=service_schema_set_mode) + + service_schema_start_flow = YEELIGHT_SERVICE_SCHEMA.extend( + YEELIGHT_FLOW_TRANSITION_SCHEMA + ) + hass.services.register( + DOMAIN, SERVICE_START_FLOW, service_handler, + schema=service_schema_start_flow) + class YeelightLight(Light): """Representation of a Yeelight light.""" @@ -455,3 +502,29 @@ def turn_off(self, **kwargs) -> None: duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s self.device.turn_off(duration=duration) + + def set_mode(self, mode: str): + """Set a power mode.""" + import yeelight + + try: + self._bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to set the power mode: %s", ex) + + self.device.update() + + def start_flow(self, transitions, count=0, action=ACTION_RECOVER): + """Start flow.""" + import yeelight + + try: + flow = yeelight.Flow( + count=count, + action=yeelight.Flow.actions[action], + transitions=transitions) + + self._bulb.start_flow(flow) + self.device.update() + except yeelight.BulbException as ex: + _LOGGER.error("Unable to set effect: %s", ex) From 17a96c6d9b80db1bd14e9ac4a68314d47339081c Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Mon, 25 Mar 2019 05:25:15 -0700 Subject: [PATCH 138/605] Improve PS4 media art fetching and config flow (#22167) * improved config flow * Added errors, docs url * Added errors, docs url * Added manual config mode * Add tests for manual/auto host input * fix inline docs * fix inline docs * Changed region list * Added deprecated region message * removed DEFAULT_REGION * Added close method * Fixes * Update const.py * Update const.py * Update const.py * Update test_config_flow.py * Added invalid pin errors * Update strings.json * Update strings.json * bump pyps4 to 0.5.0 * Bump pyps4 0.5.0 * Bump pyps4 to 0.5.0 * test fixes * pylint * Change error reference * remove pin messages * remove pin messages * Update en.json * remove pin tests * fix tests * update vol * Vol fix * Update config_flow.py * Add migration for v1 entry * lint * fixes * typo * fix * Update config_flow.py * Fix vol * Executor job for io method. * Update __init__.py * blank line * Update __init__.py * Update tests/components/ps4/test_config_flow.py Co-Authored-By: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> --- .../components/ps4/.translations/en.json | 19 ++- homeassistant/components/ps4/__init__.py | 45 ++++++- homeassistant/components/ps4/config_flow.py | 114 ++++++++++++------ homeassistant/components/ps4/const.py | 6 +- homeassistant/components/ps4/media_player.py | 11 +- homeassistant/components/ps4/strings.json | 19 ++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/ps4/test_config_flow.py | 84 ++++++++++--- 9 files changed, 229 insertions(+), 73 deletions(-) diff --git a/homeassistant/components/ps4/.translations/en.json b/homeassistant/components/ps4/.translations/en.json index c0b476ff4e2aae..662f6fb6116d75 100644 --- a/homeassistant/components/ps4/.translations/en.json +++ b/homeassistant/components/ps4/.translations/en.json @@ -4,18 +4,27 @@ "credential_error": "Error fetching credentials.", "devices_configured": "All devices found are already configured.", "no_devices_found": "No PlayStation 4 devices found on the network.", - "port_987_bind_error": "Could not bind to port 987.", - "port_997_bind_error": "Could not bind to port 997." + "port_987_bind_error": "Could not bind to port 987. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", + "port_997_bind_error": "Could not bind to port 997. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info." }, "error": { "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.", - "not_ready": "PlayStation 4 is not on or connected to network." + "not_ready": "PlayStation 4 is not on or connected to network.", + "no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure." }, "step": { "creds": { "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue.", "title": "PlayStation 4" }, + "mode": { + "data": { + "mode": "Config Mode", + "ip_address": "IP Address (Leave empty if using Auto Discovery)." + }, + "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", + "title": "PlayStation 4" + }, "link": { "data": { "code": "PIN", @@ -23,10 +32,10 @@ "name": "Name", "region": "Region" }, - "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed.", + "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", "title": "PlayStation 4" } }, "title": "PlayStation 4" } -} \ No newline at end of file +} diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index edefb5e47098eb..d5833ae1673296 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -6,13 +6,15 @@ """ import logging -from .config_flow import ( # noqa pylint: disable=unused-import - PlayStation4FlowHandler) +from homeassistant.const import CONF_REGION +from homeassistant.util import location + +from .config_flow import PlayStation4FlowHandler # noqa: pylint: disable=unused-import from .const import DOMAIN # noqa: pylint: disable=unused-import _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyps4-homeassistant==0.4.8'] +REQUIREMENTS = ['pyps4-homeassistant==0.5.0'] async def async_setup(hass, config): @@ -32,3 +34,40 @@ async def async_unload_entry(hass, entry): await hass.config_entries.async_forward_entry_unload( entry, 'media_player') return True + + +async def async_migrate_entry(hass, entry): + """Migrate old entry.""" + from pyps4_homeassistant.media_art import COUNTRIES + + config_entries = hass.config_entries + data = entry.data + version = entry.version + + reason = {1: "Region codes have changed"} # From 0.89 + + # Migrate Version 1 -> Version 2 + if version == 1: + loc = await hass.async_add_executor_job(location.detect_location_info) + if loc: + country = loc.country_name + if country in COUNTRIES: + for device in data['devices']: + device[CONF_REGION] = country + entry.version = 2 + config_entries.async_update_entry(entry, data=data) + _LOGGER.info( + "PlayStation 4 Config Updated: \ + Region changed to: %s", country) + return True + + msg = """{} for the PlayStation 4 Integration. + Please remove the PS4 Integration and re-configure + [here](/config/integrations).""".format(reason[version]) + + hass.components.persistent_notification.async_create( + title="PlayStation 4 Integration Configuration Requires Update", + message=msg, + notification_id='config_entry_migration' + ) + return False diff --git a/homeassistant/components/ps4/config_flow.py b/homeassistant/components/ps4/config_flow.py index 148b0ae6d84c53..1b184a3774fc35 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -8,10 +8,14 @@ from homeassistant.const import ( CONF_CODE, CONF_HOST, CONF_IP_ADDRESS, CONF_NAME, CONF_REGION, CONF_TOKEN) -from .const import DEFAULT_NAME, DEFAULT_REGION, DOMAIN, REGIONS +from .const import DEFAULT_NAME, DOMAIN _LOGGER = logging.getLogger(__name__) +CONF_MODE = 'Config Mode' +CONF_AUTO = "Auto Discover" +CONF_MANUAL = "Manual Entry" + UDP_PORT = 987 TCP_PORT = 997 PORT_MSG = {UDP_PORT: 'port_987_bind_error', TCP_PORT: 'port_997_bind_error'} @@ -21,7 +25,7 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow): """Handle a PlayStation 4 config flow.""" - VERSION = 1 + VERSION = 2 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL def __init__(self): @@ -34,6 +38,8 @@ def __init__(self): self.host = None self.region = None self.pin = None + self.m_device = None + self.device_list = [] async def async_step_user(self, user_input=None): """Handle a user config flow.""" @@ -46,7 +52,7 @@ async def async_step_user(self, user_input=None): return self.async_abort(reason=reason) # Skip Creds Step if a device is configured. if self.hass.config_entries.async_entries(DOMAIN): - return await self.async_step_link() + return await self.async_step_mode() return await self.async_step_creds() async def async_step_creds(self, user_input=None): @@ -56,53 +62,82 @@ async def async_step_creds(self, user_input=None): self.helper.get_creds) if self.creds is not None: - return await self.async_step_link() + return await self.async_step_mode() return self.async_abort(reason='credential_error') return self.async_show_form( step_id='creds') - async def async_step_link(self, user_input=None): - """Prompt user input. Create or edit entry.""" + async def async_step_mode(self, user_input=None): + """Prompt for mode.""" errors = {} + mode = [CONF_AUTO, CONF_MANUAL] + + if user_input is not None: + if user_input[CONF_MODE] == CONF_MANUAL: + try: + device = user_input[CONF_IP_ADDRESS] + if device: + self.m_device = device + except KeyError: + errors[CONF_IP_ADDRESS] = 'no_ipaddress' + if not errors: + return await self.async_step_link() - # Search for device. - devices = await self.hass.async_add_executor_job( - self.helper.has_devices) + mode_schema = OrderedDict() + mode_schema[vol.Required( + CONF_MODE, default=CONF_AUTO)] = vol.In(list(mode)) + mode_schema[vol.Optional(CONF_IP_ADDRESS)] = str - # Abort if can't find device. - if not devices: - return self.async_abort(reason='no_devices_found') + return self.async_show_form( + step_id='mode', + data_schema=vol.Schema(mode_schema), + errors=errors, + ) - device_list = [ - device['host-ip'] for device in devices] + async def async_step_link(self, user_input=None): + """Prompt user input. Create or edit entry.""" + from pyps4_homeassistant.media_art import COUNTRIES + regions = sorted(COUNTRIES.keys()) + errors = {} - # If entry exists check that devices found aren't configured. - if self.hass.config_entries.async_entries(DOMAIN): - creds = {} - for entry in self.hass.config_entries.async_entries(DOMAIN): - # Retrieve creds from entry - creds['data'] = entry.data[CONF_TOKEN] - # Retrieve device data from entry - conf_devices = entry.data['devices'] - for c_device in conf_devices: - if c_device['host'] in device_list: - # Remove configured device from search list. - device_list.remove(c_device['host']) - # If list is empty then all devices are configured. - if not device_list: - return self.async_abort(reason='devices_configured') - # Add existing creds for linking. Should be only 1. - if not creds: - # Abort if creds is missing. - return self.async_abort(reason='credential_error') - self.creds = creds['data'] + if user_input is None: + # Search for device. + devices = await self.hass.async_add_executor_job( + self.helper.has_devices, self.m_device) + + # Abort if can't find device. + if not devices: + return self.async_abort(reason='no_devices_found') + + self.device_list = [device['host-ip'] for device in devices] + + # If entry exists check that devices found aren't configured. + if self.hass.config_entries.async_entries(DOMAIN): + creds = {} + for entry in self.hass.config_entries.async_entries(DOMAIN): + # Retrieve creds from entry + creds['data'] = entry.data[CONF_TOKEN] + # Retrieve device data from entry + conf_devices = entry.data['devices'] + for c_device in conf_devices: + if c_device['host'] in self.device_list: + # Remove configured device from search list. + self.device_list.remove(c_device['host']) + # If list is empty then all devices are configured. + if not self.device_list: + return self.async_abort(reason='devices_configured') + # Add existing creds for linking. Should be only 1. + if not creds: + # Abort if creds is missing. + return self.async_abort(reason='credential_error') + self.creds = creds['data'] # Login to PS4 with user data. if user_input is not None: self.region = user_input[CONF_REGION] self.name = user_input[CONF_NAME] - self.pin = user_input[CONF_CODE] + self.pin = str(user_input[CONF_CODE]) self.host = user_input[CONF_IP_ADDRESS] is_ready, is_login = await self.hass.async_add_executor_job( @@ -130,10 +165,11 @@ async def async_step_link(self, user_input=None): # Show User Input form. link_schema = OrderedDict() - link_schema[vol.Required(CONF_IP_ADDRESS)] = vol.In(list(device_list)) - link_schema[vol.Required( - CONF_REGION, default=DEFAULT_REGION)] = vol.In(list(REGIONS)) - link_schema[vol.Required(CONF_CODE)] = str + link_schema[vol.Required(CONF_IP_ADDRESS)] = vol.In( + list(self.device_list)) + link_schema[vol.Required(CONF_REGION)] = vol.In(list(regions)) + link_schema[vol.Required(CONF_CODE)] = vol.All( + vol.Strip, vol.Length(min=8, max=8), vol.Coerce(int)) link_schema[vol.Required(CONF_NAME, default=DEFAULT_NAME)] = str return self.async_show_form( diff --git a/homeassistant/components/ps4/const.py b/homeassistant/components/ps4/const.py index 0618ca9675f8db..bbf654530b0081 100644 --- a/homeassistant/components/ps4/const.py +++ b/homeassistant/components/ps4/const.py @@ -1,5 +1,7 @@ """Constants for PlayStation 4.""" DEFAULT_NAME = "PlayStation 4" -DEFAULT_REGION = "R1" +DEFAULT_REGION = "United States" DOMAIN = 'ps4' -REGIONS = ('R1', 'R2', 'R3', 'R4', 'R5') + +# Deprecated used for logger/backwards compatibility from 0.89 +REGIONS = ['R1', 'R2', 'R3', 'R4', 'R5'] diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index 60b656a469dcbc..e2f0004f80e9dd 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -20,7 +20,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.json import load_json, save_json -from .const import DOMAIN as PS4_DOMAIN +from .const import DOMAIN as PS4_DOMAIN, REGIONS as deprecated_regions DEPENDENCIES = ['ps4'] @@ -142,6 +142,12 @@ def update(self): self._games = self.load_games() if self._games is not None: self._source_list = list(sorted(self._games.values())) + # Non-Breaking although data returned may be inaccurate. + if self._region in deprecated_regions: + _LOGGER.info("""Region: %s has been deprecated. + Please remove PS4 integration + and Re-configure again to utilize + current regions""", self._region) except socket.timeout: status = None if status is not None: @@ -275,6 +281,8 @@ def get_device_info(self, status): async def async_will_remove_from_hass(self): """Remove Entity from Hass.""" + # Close TCP Socket + await self.hass.async_add_executor_job(self._ps4.close) self.hass.data[PS4_DATA].devices.remove(self) @property @@ -320,6 +328,7 @@ def media_content_id(self): @property def media_content_type(self): """Content type of current playing media.""" + # No MEDIA_TYPE_GAME attr as of 0.90. return MEDIA_TYPE_MUSIC @property diff --git a/homeassistant/components/ps4/strings.json b/homeassistant/components/ps4/strings.json index 5f4e2a7c8b4186..d8fdc9e18dbcd4 100644 --- a/homeassistant/components/ps4/strings.json +++ b/homeassistant/components/ps4/strings.json @@ -6,9 +6,17 @@ "title": "PlayStation 4", "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue." }, + "mode": { + "title": "PlayStation 4", + "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", + "data": { + "mode": "Config Mode", + "ip_address": "IP Address (Leave empty if using Auto Discovery)." + }, + }, "link": { "title": "PlayStation 4", - "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed.", + "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", "data": { "region": "Region", "name": "Name", @@ -19,14 +27,15 @@ }, "error": { "not_ready": "PlayStation 4 is not on or connected to network.", - "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct." + "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.", + "no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure." }, "abort": { "credential_error": "Error fetching credentials.", "no_devices_found": "No PlayStation 4 devices found on the network.", - "devices_configured": "All devices found are already configured.", - "port_987_bind_error": "Could not bind to port 987.", - "port_997_bind_error": "Could not bind to port 997." + "devices_configured": "All devices found are already configured.", + "port_987_bind_error": "Could not bind to port 987. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", + "port_997_bind_error": "Could not bind to port 997. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info." } } } diff --git a/requirements_all.txt b/requirements_all.txt index de994fa9122131..8603d7a91de70c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1232,7 +1232,7 @@ pypoint==1.1.1 pypollencom==2.2.3 # homeassistant.components.ps4 -pyps4-homeassistant==0.4.8 +pyps4-homeassistant==0.5.0 # homeassistant.components.qwikswitch pyqwikswitch==0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fe026a3813cfec..1a8298d196b00b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -230,7 +230,7 @@ pyopenuv==1.0.9 pyotp==2.2.6 # homeassistant.components.ps4 -pyps4-homeassistant==0.4.8 +pyps4-homeassistant==0.5.0 # homeassistant.components.qwikswitch pyqwikswitch==0.8 diff --git a/tests/components/ps4/test_config_flow.py b/tests/components/ps4/test_config_flow.py index 271db46d856b47..06fe1ef65da8f7 100644 --- a/tests/components/ps4/test_config_flow.py +++ b/tests/components/ps4/test_config_flow.py @@ -44,6 +44,9 @@ MOCK_UDP_PORT = int(987) MOCK_TCP_PORT = int(997) +MOCK_AUTO = {"Config Mode": 'Auto Discover'} +MOCK_MANUAL = {"Config Mode": 'Manual Entry', CONF_IP_ADDRESS: MOCK_HOST} + async def test_full_flow_implementation(hass): """Test registering an implementation and flow works.""" @@ -58,13 +61,18 @@ async def test_full_flow_implementation(hass): assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'creds' - # Step Creds results with form in Step Link. + # Step Creds results with form in Step Mode. with patch('pyps4_homeassistant.Helper.get_creds', - return_value=MOCK_CREDS), \ - patch('pyps4_homeassistant.Helper.has_devices', - return_value=[{'host-ip': MOCK_HOST}]): + return_value=MOCK_CREDS): result = await flow.async_step_creds({}) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mode' + + # Step Mode with User Input which is not manual, results in Step Link. + with patch('pyps4_homeassistant.Helper.has_devices', + return_value=[{'host-ip': MOCK_HOST}]): + result = await flow.async_step_mode(MOCK_AUTO) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'link' # User Input results in created entry. @@ -78,6 +86,8 @@ async def test_full_flow_implementation(hass): assert result['data']['devices'] == [MOCK_DEVICE] assert result['title'] == MOCK_TITLE + await hass.async_block_till_done() + # Add entry using result data. mock_data = { CONF_TOKEN: result['data'][CONF_TOKEN], @@ -104,14 +114,19 @@ async def test_multiple_flow_implementation(hass): assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'creds' - # Step Creds results with form in Step Link. + # Step Creds results with form in Step Mode. with patch('pyps4_homeassistant.Helper.get_creds', - return_value=MOCK_CREDS), \ - patch('pyps4_homeassistant.Helper.has_devices', - return_value=[{'host-ip': MOCK_HOST}, - {'host-ip': MOCK_HOST_ADDITIONAL}]): + return_value=MOCK_CREDS): result = await flow.async_step_creds({}) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mode' + + # Step Mode with User Input which is not manual, results in Step Link. + with patch('pyps4_homeassistant.Helper.has_devices', + return_value=[{'host-ip': MOCK_HOST}, + {'host-ip': MOCK_HOST_ADDITIONAL}]): + result = await flow.async_step_mode(MOCK_AUTO) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'link' # User Input results in created entry. @@ -142,7 +157,7 @@ async def test_multiple_flow_implementation(hass): # Test additional flow. - # User Step Started, results in Step Link: + # User Step Started, results in Step Mode: with patch('pyps4_homeassistant.Helper.port_bind', return_value=None), \ patch('pyps4_homeassistant.Helper.has_devices', @@ -150,6 +165,14 @@ async def test_multiple_flow_implementation(hass): {'host-ip': MOCK_HOST_ADDITIONAL}]): result = await flow.async_step_user() assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mode' + + # Step Mode with User Input which is not manual, results in Step Link. + with patch('pyps4_homeassistant.Helper.has_devices', + return_value=[{'host-ip': MOCK_HOST}, + {'host-ip': MOCK_HOST_ADDITIONAL}]): + result = await flow.async_step_mode(MOCK_AUTO) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'link' # Step Link @@ -158,12 +181,14 @@ async def test_multiple_flow_implementation(hass): {'host-ip': MOCK_HOST_ADDITIONAL}]), \ patch('pyps4_homeassistant.Helper.link', return_value=(True, True)): - result = await flow.async_step_link(user_input=MOCK_CONFIG_ADDITIONAL) + result = await flow.async_step_link(MOCK_CONFIG_ADDITIONAL) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['data'][CONF_TOKEN] == MOCK_CREDS assert len(result['data']['devices']) == 1 assert result['title'] == MOCK_TITLE + await hass.async_block_till_done() + mock_data = { CONF_TOKEN: result['data'][CONF_TOKEN], 'devices': result['data']['devices']} @@ -230,7 +255,7 @@ async def test_additional_device(hass): {'host-ip': MOCK_HOST_ADDITIONAL}]), \ patch('pyps4_homeassistant.Helper.link', return_value=(True, True)): - result = await flow.async_step_link(user_input=MOCK_CONFIG_ADDITIONAL) + result = await flow.async_step_link(MOCK_CONFIG_ADDITIONAL) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['data'][CONF_TOKEN] == MOCK_CREDS assert len(result['data']['devices']) == 1 @@ -249,12 +274,26 @@ async def test_no_devices_found_abort(hass): flow = ps4.PlayStation4FlowHandler() flow.hass = hass - with patch('pyps4_homeassistant.Helper.has_devices', return_value=None): - result = await flow.async_step_link(MOCK_CONFIG) + with patch('pyps4_homeassistant.Helper.has_devices', return_value=[]): + result = await flow.async_step_link() assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT assert result['reason'] == 'no_devices_found' +async def test_manual_mode(hass): + """Test host specified in manual mode is passed to Step Link.""" + flow = ps4.PlayStation4FlowHandler() + flow.hass = hass + + # Step Mode with User Input: manual, results in Step Link. + with patch('pyps4_homeassistant.Helper.has_devices', + return_value=[{'host-ip': flow.m_device}]): + result = await flow.async_step_mode(MOCK_MANUAL) + assert flow.m_device == MOCK_HOST + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'link' + + async def test_credential_abort(hass): """Test that failure to get credentials aborts flow.""" flow = ps4.PlayStation4FlowHandler() @@ -266,8 +305,8 @@ async def test_credential_abort(hass): assert result['reason'] == 'credential_error' -async def test_invalid_pin_error(hass): - """Test that invalid pin throws an error.""" +async def test_wrong_pin_error(hass): + """Test that incorrect pin throws an error.""" flow = ps4.PlayStation4FlowHandler() flow.hass = hass @@ -294,3 +333,16 @@ async def test_device_connection_error(hass): assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'link' assert result['errors'] == {'base': 'not_ready'} + + +async def test_manual_mode_no_ip_error(hass): + """Test no IP specified in manual mode throws an error.""" + flow = ps4.PlayStation4FlowHandler() + flow.hass = hass + + mock_input = {"Config Mode": 'Manual Entry'} + + result = await flow.async_step_mode(mock_input) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'mode' + assert result['errors'] == {CONF_IP_ADDRESS: 'no_ipaddress'} From c8048e1aff7063e0301a208783a9fc939d05a100 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 25 Mar 2019 05:37:10 -0700 Subject: [PATCH 139/605] Allow for custom turn on/off commands (#22354) --- .../components/androidtv/media_player.py | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 1d186484f40543..2db3110f2d9d46 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -40,6 +40,8 @@ CONF_ADB_SERVER_PORT = 'adb_server_port' CONF_APPS = 'apps' CONF_GET_SOURCES = 'get_sources' +CONF_TURN_ON_COMMAND = 'turn_on_command' +CONF_TURN_OFF_COMMAND = 'turn_off_command' DEFAULT_NAME = 'Android TV' DEFAULT_PORT = 5555 @@ -79,7 +81,9 @@ def has_adb_files(value): cv.port, vol.Optional(CONF_GET_SOURCES, default=DEFAULT_GET_SOURCES): cv.boolean, vol.Optional(CONF_APPS, default=dict()): - vol.Schema({cv.string: cv.string}) + vol.Schema({cv.string: cv.string}), + vol.Optional(CONF_TURN_ON_COMMAND): cv.string, + vol.Optional(CONF_TURN_OFF_COMMAND): cv.string }) # Translate from `AndroidTV` / `FireTV` reported state to HA state. @@ -136,12 +140,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None): else: if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV: device = AndroidTVDevice(aftv, config[CONF_NAME], - config[CONF_APPS]) + config[CONF_APPS], + config.get(CONF_TURN_ON_COMMAND), + config.get(CONF_TURN_OFF_COMMAND)) device_name = config[CONF_NAME] if CONF_NAME in config \ else 'Android TV' else: device = FireTVDevice(aftv, config[CONF_NAME], config[CONF_APPS], - config[CONF_GET_SOURCES]) + config[CONF_GET_SOURCES], + config.get(CONF_TURN_ON_COMMAND), + config.get(CONF_TURN_OFF_COMMAND)) device_name = config[CONF_NAME] if CONF_NAME in config \ else 'Fire TV' @@ -199,7 +207,8 @@ def _adb_exception_catcher(self, *args, **kwargs): class ADBDevice(MediaPlayerDevice): """Representation of an Android TV or Fire TV device.""" - def __init__(self, aftv, name, apps): + def __init__(self, aftv, name, apps, turn_on_command, + turn_off_command): """Initialize the Android TV / Fire TV device.""" from androidtv.constants import APPS, KEYS @@ -209,6 +218,9 @@ def __init__(self, aftv, name, apps): self._apps.update(apps) self._keys = KEYS + self.turn_on_command = turn_on_command + self.turn_off_command = turn_off_command + # ADB exceptions to catch if not self.aftv.adb_server_ip: # Using "python-adb" (Python ADB implementation) @@ -278,12 +290,18 @@ def media_play_pause(self): @adb_decorator() def turn_on(self): """Turn on the device.""" - self.aftv.turn_on() + if self.turn_on_command: + self.aftv.adb_shell(self.turn_on_command) + else: + self.aftv.turn_on() @adb_decorator() def turn_off(self): """Turn off the device.""" - self.aftv.turn_off() + if self.turn_off_command: + self.aftv.adb_shell(self.turn_off_command) + else: + self.aftv.turn_off() @adb_decorator() def media_previous_track(self): @@ -311,9 +329,11 @@ def adb_command(self, cmd): class AndroidTVDevice(ADBDevice): """Representation of an Android TV device.""" - def __init__(self, aftv, name, apps): + def __init__(self, aftv, name, apps, turn_on_command, + turn_off_command): """Initialize the Android TV device.""" - super().__init__(aftv, name, apps) + super().__init__(aftv, name, apps, turn_on_command, + turn_off_command) self._device = None self._muted = None @@ -392,9 +412,11 @@ def volume_up(self): class FireTVDevice(ADBDevice): """Representation of a Fire TV device.""" - def __init__(self, aftv, name, apps, get_sources): + def __init__(self, aftv, name, apps, get_sources, + turn_on_command, turn_off_command): """Initialize the Fire TV device.""" - super().__init__(aftv, name, apps) + super().__init__(aftv, name, apps, turn_on_command, + turn_off_command) self._get_sources = get_sources self._running_apps = None From f99795705439b8fddf8d50369cc0414d21c1ca01 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Mon, 25 Mar 2019 15:28:34 +0000 Subject: [PATCH 140/605] Remove unused const (#22383) --- homeassistant/components/homekit_controller/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index b16f82de7f6482..0cb9ecbfc0789b 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -28,7 +28,6 @@ _LOGGER = logging.getLogger(__name__) -REQUEST_TIMEOUT = 5 # seconds RETRY_INTERVAL = 60 # seconds PAIRING_FILE = "pairing.json" From b57d809dadaeb9b80b96f797917bb02613bbbbda Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 25 Mar 2019 17:43:15 +0100 Subject: [PATCH 141/605] Update hass-nabucasa & fix state (#22385) * Update hass-nabucasa & fix state * Fix lint --- homeassistant/components/cloud/__init__.py | 2 +- homeassistant/components/cloud/binary_sensor.py | 12 ++++++++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_binary_sensor.py | 3 +++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 9517971b16d8d1..76a768385f8508 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.10'] +REQUIREMENTS = ['hass-nabucasa==0.11'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/cloud/binary_sensor.py b/homeassistant/components/cloud/binary_sensor.py index 874c3420c5844e..19a6528e3218f5 100644 --- a/homeassistant/components/cloud/binary_sensor.py +++ b/homeassistant/components/cloud/binary_sensor.py @@ -1,6 +1,7 @@ """Support for Home Assistant Cloud binary sensors.""" +import asyncio + from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN @@ -8,6 +9,9 @@ DEPENDENCIES = ['cloud'] +WAIT_UNTIL_CHANGE = 3 + + async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): """Set up the cloud binary sensors.""" @@ -58,10 +62,10 @@ def should_poll(self) -> bool: async def async_added_to_hass(self): """Register update dispatcher.""" - @callback - def async_state_update(data): + async def async_state_update(data): """Update callback.""" - self.async_write_ha_state() + await asyncio.sleep(WAIT_UNTIL_CHANGE) + self.async_schedule_update_ha_state() self._unsub_dispatcher = async_dispatcher_connect( self.hass, DISPATCHER_REMOTE_UPDATE, async_state_update) diff --git a/requirements_all.txt b/requirements_all.txt index 8603d7a91de70c..020ed3248a5a52 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -521,7 +521,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.10 +hass-nabucasa==0.11 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1a8298d196b00b..4bd50b713e2fc8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -120,7 +120,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.10 +hass-nabucasa==0.11 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py index 938829b809bdb6..f6d8783a609a71 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -7,6 +7,9 @@ async def test_remote_connection_sensor(hass): """Test the remote connection sensor.""" + from homeassistant.components.cloud import binary_sensor as bin_sensor + bin_sensor.WAIT_UNTIL_CHANGE = 0 + assert await async_setup_component(hass, 'cloud', {'cloud': {}}) cloud = hass.data['cloud'] = Mock() cloud.remote.certificate = None From f1a0ad9e4ab7bdc8ab76ba8bcf6f1eb7a39dadb5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 25 Mar 2019 10:04:35 -0700 Subject: [PATCH 142/605] Frontend indicate require admin (#22272) * Allow panels to indicate they are meant for admins * Panels to indicate when they require admin access * Do not return admin-only panels to non-admin users * Fix flake8 --- homeassistant/components/config/__init__.py | 2 +- homeassistant/components/frontend/__init__.py | 26 ++++++++++++------ homeassistant/components/hassio/__init__.py | 1 + .../components/panel_custom/__init__.py | 10 +++++-- .../components/panel_iframe/__init__.py | 5 +++- tests/components/frontend/test_init.py | 27 ++++++++++++++++++- tests/components/hassio/test_init.py | 23 ++++++++++++++++ tests/components/panel_custom/test_init.py | 2 ++ tests/components/panel_iframe/test_init.py | 8 +++++- 9 files changed, 90 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py index efabd03b58608c..0366dfa2b8bfeb 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -32,7 +32,7 @@ async def async_setup(hass, config): """Set up the config component.""" await hass.components.frontend.async_register_built_in_panel( - 'config', 'config', 'hass:settings') + 'config', 'config', 'hass:settings', require_admin=True) async def setup_panel(panel_name): """Set up a panel.""" diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 30b9d350df6d1d..5a10b60f12f3ad 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -123,14 +123,18 @@ class Panel: # Config to pass to the webcomponent config = None + # If the panel should only be visible to admins + require_admin = False + def __init__(self, component_name, sidebar_title, sidebar_icon, - frontend_url_path, config): + frontend_url_path, config, require_admin): """Initialize a built-in panel.""" self.component_name = component_name self.sidebar_title = sidebar_title self.sidebar_icon = sidebar_icon self.frontend_url_path = frontend_url_path or component_name self.config = config + self.require_admin = require_admin @callback def async_register_index_routes(self, router, index_view): @@ -150,16 +154,18 @@ def to_response(self): 'title': self.sidebar_title, 'config': self.config, 'url_path': self.frontend_url_path, + 'require_admin': self.require_admin, } @bind_hass async def async_register_built_in_panel(hass, component_name, sidebar_title=None, sidebar_icon=None, - frontend_url_path=None, config=None): + frontend_url_path=None, config=None, + require_admin=False): """Register a built-in panel.""" panel = Panel(component_name, sidebar_title, sidebar_icon, - frontend_url_path, config) + frontend_url_path, config, require_admin) panels = hass.data.get(DATA_PANELS) if panels is None: @@ -247,9 +253,11 @@ def async_finalize_panel(panel): await asyncio.wait( [async_register_built_in_panel(hass, panel) for panel in ( - 'dev-event', 'dev-info', 'dev-service', 'dev-state', - 'dev-template', 'dev-mqtt', 'kiosk', 'states', 'profile')], - loop=hass.loop) + 'kiosk', 'states', 'profile')], loop=hass.loop) + await asyncio.wait( + [async_register_built_in_panel(hass, panel, require_admin=True) + for panel in ('dev-event', 'dev-info', 'dev-service', 'dev-state', + 'dev-template', 'dev-mqtt')], loop=hass.loop) hass.data[DATA_FINALIZE_PANEL] = async_finalize_panel @@ -478,9 +486,11 @@ def websocket_get_panels(hass, connection, msg): Async friendly. """ + user_is_admin = connection.user.is_admin panels = { - panel: connection.hass.data[DATA_PANELS][panel].to_response() - for panel in connection.hass.data[DATA_PANELS]} + panel_key: panel.to_response() + for panel_key, panel in connection.hass.data[DATA_PANELS].items() + if user_is_admin or not panel.require_admin} connection.send_message(websocket_api.result_message( msg['id'], panels)) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 7f85c8cfc3fc66..7e47ac152e32c9 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -189,6 +189,7 @@ async def async_setup(hass, config): sidebar_icon='hass:home-assistant', js_url='/api/hassio/app/entrypoint.js', embed_iframe=True, + require_admin=True, ) await hassio.update_hass_api(config.get('http', {}), refresh_token.token) diff --git a/homeassistant/components/panel_custom/__init__.py b/homeassistant/components/panel_custom/__init__.py index 2fce5d9857c99d..7fe2191f4c497c 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -23,6 +23,7 @@ CONF_EMBED_IFRAME = 'embed_iframe' CONF_TRUST_EXTERNAL_SCRIPT = 'trust_external_script' CONF_URL_EXCLUSIVE_GROUP = 'url_exclusive_group' +CONF_REQUIRE_ADMIN = 'require_admin' MSG_URL_CONFLICT = \ 'Pass in only one of webcomponent_path, module_url or js_url' @@ -52,6 +53,7 @@ default=DEFAULT_EMBED_IFRAME): cv.boolean, vol.Optional(CONF_TRUST_EXTERNAL_SCRIPT, default=DEFAULT_TRUST_EXTERNAL): cv.boolean, + vol.Optional(CONF_REQUIRE_ADMIN, default=False): cv.boolean, })]) }, extra=vol.ALLOW_EXTRA) @@ -77,7 +79,9 @@ async def async_register_panel( # Should user be asked for confirmation when loading external source trust_external=DEFAULT_TRUST_EXTERNAL, # Configuration to be passed to the panel - config=None): + config=None, + # If your panel should only be shown to admin users + require_admin=False): """Register a new custom panel.""" if js_url is None and html_url is None and module_url is None: raise ValueError('Either js_url, module_url or html_url is required.') @@ -115,7 +119,8 @@ async def async_register_panel( sidebar_title=sidebar_title, sidebar_icon=sidebar_icon, frontend_url_path=frontend_url_path, - config=config + config=config, + require_admin=require_admin, ) @@ -134,6 +139,7 @@ async def async_setup(hass, config): 'config': panel.get(CONF_CONFIG), 'trust_external': panel[CONF_TRUST_EXTERNAL_SCRIPT], 'embed_iframe': panel[CONF_EMBED_IFRAME], + 'require_admin': panel[CONF_REQUIRE_ADMIN], } panel_path = panel.get(CONF_WEBCOMPONENT_PATH) diff --git a/homeassistant/components/panel_iframe/__init__.py b/homeassistant/components/panel_iframe/__init__.py index b82f9fa9789421..9319dfcc6adb27 100644 --- a/homeassistant/components/panel_iframe/__init__.py +++ b/homeassistant/components/panel_iframe/__init__.py @@ -12,6 +12,7 @@ CONF_RELATIVE_URL_ERROR_MSG = "Invalid relative URL. Absolute path required." CONF_RELATIVE_URL_REGEX = r'\A/' +CONF_REQUIRE_ADMIN = 'require_admin' CONFIG_SCHEMA = vol.Schema({ DOMAIN: cv.schema_with_slug_keys( @@ -19,6 +20,7 @@ # pylint: disable=no-value-for-parameter vol.Optional(CONF_TITLE): cv.string, vol.Optional(CONF_ICON): cv.icon, + vol.Optional(CONF_REQUIRE_ADMIN, default=False): cv.boolean, vol.Required(CONF_URL): vol.Any( vol.Match( CONF_RELATIVE_URL_REGEX, @@ -34,6 +36,7 @@ async def async_setup(hass, config): for url_path, info in config[DOMAIN].items(): await hass.components.frontend.async_register_built_in_panel( 'iframe', info.get(CONF_TITLE), info.get(CONF_ICON), - url_path, {'url': info[CONF_URL]}) + url_path, {'url': info[CONF_URL]}, + require_admin=info[CONF_REQUIRE_ADMIN]) return True diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index b1b9a70d5945ae..e4ed2c15ecb699 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -249,7 +249,7 @@ async def test_get_panels(hass, hass_ws_client): """Test get_panels command.""" await async_setup_component(hass, 'frontend') await hass.components.frontend.async_register_built_in_panel( - 'map', 'Map', 'mdi:tooltip-account') + 'map', 'Map', 'mdi:tooltip-account', require_admin=True) client = await hass_ws_client(hass) await client.send_json({ @@ -266,6 +266,31 @@ async def test_get_panels(hass, hass_ws_client): assert msg['result']['map']['url_path'] == 'map' assert msg['result']['map']['icon'] == 'mdi:tooltip-account' assert msg['result']['map']['title'] == 'Map' + assert msg['result']['map']['require_admin'] is True + + +async def test_get_panels_non_admin(hass, hass_ws_client, hass_admin_user): + """Test get_panels command.""" + hass_admin_user.groups = [] + await async_setup_component(hass, 'frontend') + await hass.components.frontend.async_register_built_in_panel( + 'map', 'Map', 'mdi:tooltip-account', require_admin=True) + await hass.components.frontend.async_register_built_in_panel( + 'history', 'History', 'mdi:history') + + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 5, + 'type': 'get_panels', + }) + + msg = await client.receive_json() + + assert msg['id'] == 5 + assert msg['type'] == TYPE_RESULT + assert msg['success'] + assert 'history' in msg['result'] + assert 'map' not in msg['result'] async def test_get_translations(hass, hass_ws_client): diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 1326805fc93944..ba642b698f7afb 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -8,6 +8,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.setup import async_setup_component from homeassistant.components.hassio import STORAGE_KEY +from homeassistant.components import frontend from tests.common import mock_coro @@ -44,6 +45,28 @@ def test_setup_api_ping(hass, aioclient_mock): assert hass.components.hassio.is_hassio() +async def test_setup_api_panel(hass, aioclient_mock): + """Test setup with API ping.""" + assert await async_setup_component(hass, 'frontend', {}) + with patch.dict(os.environ, MOCK_ENVIRON): + result = await async_setup_component(hass, 'hassio', {}) + assert result + + panels = hass.data[frontend.DATA_PANELS] + + assert panels.get('hassio').to_response() == { + 'component_name': 'custom', + 'icon': 'hass:home-assistant', + 'title': 'Hass.io', + 'url_path': 'hassio', + 'require_admin': True, + 'config': {'_panel_custom': {'embed_iframe': True, + 'js_url': '/api/hassio/app/entrypoint.js', + 'name': 'hassio-main', + 'trust_external': False}}, + } + + @asyncio.coroutine def test_setup_api_push_api_data(hass, aioclient_mock): """Test setup with API push.""" diff --git a/tests/components/panel_custom/test_init.py b/tests/components/panel_custom/test_init.py index c265324179dc45..8c95f96085ac2c 100644 --- a/tests/components/panel_custom/test_init.py +++ b/tests/components/panel_custom/test_init.py @@ -130,6 +130,7 @@ async def test_module_webcomponent(hass): }, 'embed_iframe': True, 'trust_external_script': True, + 'require_admin': True, } } @@ -145,6 +146,7 @@ async def test_module_webcomponent(hass): panel = panels['nice_url'] + assert panel.require_admin assert panel.config == { 'hello': 'world', '_panel_custom': { diff --git a/tests/components/panel_iframe/test_init.py b/tests/components/panel_iframe/test_init.py index cb868f64b58789..da7878399d42b9 100644 --- a/tests/components/panel_iframe/test_init.py +++ b/tests/components/panel_iframe/test_init.py @@ -41,11 +41,13 @@ def test_correct_config(self): 'icon': 'mdi:network-wireless', 'title': 'Router', 'url': 'http://192.168.1.1', + 'require_admin': True, }, 'weather': { 'icon': 'mdi:weather', 'title': 'Weather', 'url': 'https://www.wunderground.com/us/ca/san-diego', + 'require_admin': True, }, 'api': { 'icon': 'mdi:weather', @@ -67,7 +69,8 @@ def test_correct_config(self): 'config': {'url': 'http://192.168.1.1'}, 'icon': 'mdi:network-wireless', 'title': 'Router', - 'url_path': 'router' + 'url_path': 'router', + 'require_admin': True, } assert panels.get('weather').to_response() == { @@ -76,6 +79,7 @@ def test_correct_config(self): 'icon': 'mdi:weather', 'title': 'Weather', 'url_path': 'weather', + 'require_admin': True, } assert panels.get('api').to_response() == { @@ -84,6 +88,7 @@ def test_correct_config(self): 'icon': 'mdi:weather', 'title': 'Api', 'url_path': 'api', + 'require_admin': False, } assert panels.get('ftp').to_response() == { @@ -92,4 +97,5 @@ def test_correct_config(self): 'icon': 'mdi:weather', 'title': 'FTP', 'url_path': 'ftp', + 'require_admin': False, } From a59487a438f653063c254a7133dc6c2297877e3c Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 25 Mar 2019 20:25:49 +0300 Subject: [PATCH 143/605] Fix TpLink Device Tracker initialize error (#22349) Catch RequestException instead of ConnectionError In some cases TpLinkDeviceScanner throws various successors of RequestException. They should be caught. --- homeassistant/components/tplink/device_tracker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tplink/device_tracker.py b/homeassistant/components/tplink/device_tracker.py index 78f16a82d56069..33a5d5f32f8310 100644 --- a/homeassistant/components/tplink/device_tracker.py +++ b/homeassistant/components/tplink/device_tracker.py @@ -77,8 +77,8 @@ def __init__(self, config): self.last_results = {} self.success_init = self._update_info() - except requests.exceptions.ConnectionError: - _LOGGER.debug("ConnectionError in TplinkDeviceScanner") + except requests.exceptions.RequestException: + _LOGGER.debug("RequestException in %s", __class__.__name__) def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" @@ -123,8 +123,8 @@ def __init__(self, config): self.success_init = False try: self.success_init = self._update_info() - except requests.exceptions.ConnectionError: - _LOGGER.debug("ConnectionError in Tplink1DeviceScanner") + except requests.exceptions.RequestException: + _LOGGER.debug("RequestException in %s", __class__.__name__) def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" From 2731777c7eb183426d309332dbfe10fd5d19531f Mon Sep 17 00:00:00 2001 From: lapy Date: Mon, 25 Mar 2019 17:52:53 +0000 Subject: [PATCH 144/605] Add traccar events (#22348) * Add import_events(), small refactoring, traccar_id I isolated the code that imports traccar tracking data and added a new function that imports the traccar events to make them run in parallel and reduce delay. The events that are imported in hass, will be fired with the prefix "traccar_". Furthermore a traccar_id is now imported in hass entities, useful for matching the traccar hass entities with the traccar hass events in the most accurate way. * bump pytraccar version * Code format fix * Code format fix 2 * Code format fix 3 * Implement requested changes * Add new traccar dependency * Fix line too long * Update device_tracker.py * Update requirements_all.txt --- .../components/traccar/device_tracker.py | 85 +++++++++++++++++-- requirements_all.txt | 3 +- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/traccar/device_tracker.py b/homeassistant/components/traccar/device_tracker.py index 1447f7c896c76d..e3ac14279414c9 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -4,7 +4,7 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/device_tracker.traccar/ """ -from datetime import timedelta +from datetime import datetime, timedelta import logging import voluptuous as vol @@ -13,14 +13,15 @@ from homeassistant.const import ( CONF_HOST, CONF_PORT, CONF_SSL, CONF_VERIFY_SSL, CONF_PASSWORD, CONF_USERNAME, ATTR_BATTERY_LEVEL, - CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS) + CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS, + CONF_EVENT) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.event import async_track_time_interval from homeassistant.util import slugify -REQUIREMENTS = ['pytraccar==0.3.0'] +REQUIREMENTS = ['pytraccar==0.5.0', 'stringcase==1.2.0'] _LOGGER = logging.getLogger(__name__) @@ -30,6 +31,25 @@ ATTR_MOTION = 'motion' ATTR_SPEED = 'speed' ATTR_TRACKER = 'tracker' +ATTR_TRACCAR_ID = 'traccar_id' + +EVENT_DEVICE_MOVING = 'device_moving' +EVENT_COMMAND_RESULT = 'command_result' +EVENT_DEVICE_FUEL_DROP = 'device_fuel_drop' +EVENT_GEOFENCE_ENTER = 'geofence_enter' +EVENT_DEVICE_OFFLINE = 'device_offline' +EVENT_DRIVER_CHANGED = 'driver_changed' +EVENT_GEOFENCE_EXIT = 'geofence_exit' +EVENT_DEVICE_OVERSPEED = 'device_overspeed' +EVENT_DEVICE_ONLINE = 'device_online' +EVENT_DEVICE_STOPPED = 'device_stopped' +EVENT_MAINTENANCE = 'maintenance' +EVENT_ALARM = 'alarm' +EVENT_TEXT_MESSAGE = 'text_message' +EVENT_DEVICE_UNKNOWN = 'device_unknown' +EVENT_IGNITION_OFF = 'ignition_off' +EVENT_IGNITION_ON = 'ignition_on' +EVENT_ALL_EVENTS = 'all_events' DEFAULT_SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL @@ -43,6 +63,25 @@ vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, vol.Optional(CONF_MONITORED_CONDITIONS, default=[]): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_EVENT, + default=[]): vol.All(cv.ensure_list, + [vol.Any(EVENT_DEVICE_MOVING, + EVENT_COMMAND_RESULT, + EVENT_DEVICE_FUEL_DROP, + EVENT_GEOFENCE_ENTER, + EVENT_DEVICE_OFFLINE, + EVENT_DRIVER_CHANGED, + EVENT_GEOFENCE_EXIT, + EVENT_DEVICE_OVERSPEED, + EVENT_DEVICE_ONLINE, + EVENT_DEVICE_STOPPED, + EVENT_MAINTENANCE, + EVENT_ALARM, + EVENT_TEXT_MESSAGE, + EVENT_DEVICE_UNKNOWN, + EVENT_IGNITION_OFF, + EVENT_IGNITION_ON, + EVENT_ALL_EVENTS)]), }) @@ -58,7 +97,7 @@ async def async_setup_scanner(hass, config, async_see, discovery_info=None): scanner = TraccarScanner( api, hass, async_see, config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL), - config[CONF_MONITORED_CONDITIONS]) + config[CONF_MONITORED_CONDITIONS], config[CONF_EVENT]) return await scanner.async_init() @@ -66,8 +105,12 @@ async def async_setup_scanner(hass, config, async_see, discovery_info=None): class TraccarScanner: """Define an object to retrieve Traccar data.""" - def __init__(self, api, hass, async_see, scan_interval, custom_attributes): + def __init__(self, api, hass, async_see, scan_interval, + custom_attributes, + event_types): """Initialize.""" + from stringcase import camelcase + self._event_types = {camelcase(evt): evt for evt in event_types} self._custom_attributes = custom_attributes self._scan_interval = scan_interval self._async_see = async_see @@ -89,6 +132,12 @@ async def _async_update(self, now=None): """Update info from Traccar.""" _LOGGER.debug('Updating device data.') await self._api.get_device_info(self._custom_attributes) + self._hass.async_create_task(self.import_device_data()) + if self._event_types: + self._hass.async_create_task(self.import_events()) + + async def import_device_data(self): + """Import device data from Traccar.""" for devicename in self._api.device_info: device = self._api.device_info[devicename] attr = {} @@ -105,6 +154,8 @@ async def _async_update(self, now=None): attr[ATTR_BATTERY_LEVEL] = device['battery'] if device.get('motion') is not None: attr[ATTR_MOTION] = device['motion'] + if device.get('traccar_id') is not None: + attr[ATTR_TRACCAR_ID] = device['traccar_id'] for custom_attr in self._custom_attributes: if device.get(custom_attr) is not None: attr[custom_attr] = device[custom_attr] @@ -112,3 +163,27 @@ async def _async_update(self, now=None): dev_id=slugify(device['device_id']), gps=(device.get('latitude'), device.get('longitude')), attributes=attr) + + async def import_events(self): + """Import events from Traccar.""" + device_ids = [device['id'] for device in self._api.devices] + end_interval = datetime.utcnow() + start_interval = end_interval - self._scan_interval + events = await self._api.get_events( + device_ids=device_ids, + from_time=start_interval, + to_time=end_interval, + event_types=self._event_types.keys()) + if events is not None: + for event in events: + device_name = next(( + dev.get('name') for dev in self._api.devices() + if dev.get('id') == event['deviceId']), None) + self._hass.bus.async_fire( + 'traccar_' + self._event_types.get(event["type"]), { + 'device_traccar_id': event['deviceId'], + 'device_name': device_name, + 'type': event['type'], + 'serverTime': event['serverTime'], + 'attributes': event['attributes'] + }) diff --git a/requirements_all.txt b/requirements_all.txt index 020ed3248a5a52..7fd0c906ed7fd7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1433,7 +1433,7 @@ pytile==2.0.6 pytouchline==0.7 # homeassistant.components.traccar.device_tracker -pytraccar==0.3.0 +pytraccar==0.5.0 # homeassistant.components.trackr.device_tracker pytrackr==0.0.5 @@ -1653,6 +1653,7 @@ statsd==3.2.1 steamodd==4.21 # homeassistant.components.thermoworks_smoke.sensor +# homeassistant.components.traccar.device_tracker stringcase==1.2.0 # homeassistant.components.ecovacs From 6a74c403c0ee6f9252ccfa434b29bcf2761c647d Mon Sep 17 00:00:00 2001 From: zewelor Date: Mon, 25 Mar 2019 19:06:43 +0100 Subject: [PATCH 145/605] Update python yeelight and add nightlight mode sensor (#22345) --- homeassistant/components/yeelight/__init__.py | 29 +++++----- .../components/yeelight/binary_sensor.py | 57 +++++++++++++++++++ requirements_all.txt | 2 +- 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/yeelight/binary_sensor.py diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index ed4e704a6a51be..8a5c1e81a93fcc 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -8,13 +8,15 @@ from homeassistant.const import CONF_DEVICES, CONF_NAME, CONF_SCAN_INTERVAL, \ CONF_HOST, ATTR_ENTITY_ID from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN +from homeassistant.components.binary_sensor import DOMAIN as \ + BINARY_SENSOR_DOMAIN from homeassistant.helpers import discovery from homeassistant.helpers.discovery import load_platform import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['yeelight==0.4.3'] +REQUIREMENTS = ['yeelight==0.4.4'] _LOGGER = logging.getLogger(__name__) @@ -40,9 +42,6 @@ ACTION_STAY = 'stay' ACTION_OFF = 'off' -MODE_MOONLIGHT = 'moonlight' -MODE_DAYLIGHT = 'normal' - SCAN_INTERVAL = timedelta(seconds=30) YEELIGHT_RGB_TRANSITION = 'RGBTransition' @@ -90,11 +89,6 @@ vol.Required(ATTR_ENTITY_ID): cv.entity_ids, }) -NIGHTLIGHT_SUPPORTED_MODELS = [ - "ceiling3", - 'ceiling4' -] - UPDATE_REQUEST_PROPERTIES = [ "power", "bright", @@ -103,8 +97,7 @@ "hue", "sat", "color_mode", - "flowing", - "music_on", + "bg_power", "nl_br", "active_mode", ] @@ -195,6 +188,8 @@ def _setup_device(hass, hass_config, ipaddr, device_config): ) load_platform(hass, LIGHT_DOMAIN, DOMAIN, platform_config, hass_config) + load_platform(hass, BINARY_SENSOR_DOMAIN, DOMAIN, platform_config, + hass_config) class YeelightDevice: @@ -218,7 +213,7 @@ def bulb(self): self._bulb_device = yeelight.Bulb(self._ipaddr, model=self._model) # force init for type - self._update_properties() + self.update() except yeelight.BulbException as ex: _LOGGER.error("Failed to connect to bulb %s, %s: %s", @@ -226,9 +221,6 @@ def bulb(self): return self._bulb_device - def _update_properties(self): - self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) - @property def name(self): """Return the name of the device if any.""" @@ -252,6 +244,11 @@ def is_nightlight_enabled(self) -> bool: return self.bulb.last_properties.get('active_mode') == '1' + @property + def is_nightlight_supported(self) -> bool: + """Return true / false if nightlight is supported.""" + return self.bulb.get_model_specs().get('night_light', False) + def turn_on(self, duration=DEFAULT_TRANSITION): """Turn on device.""" import yeelight @@ -281,5 +278,5 @@ def update(self): if not self.bulb: return - self._update_properties() + self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) diff --git a/homeassistant/components/yeelight/binary_sensor.py b/homeassistant/components/yeelight/binary_sensor.py new file mode 100644 index 00000000000000..cf7bbc5244ecc0 --- /dev/null +++ b/homeassistant/components/yeelight/binary_sensor.py @@ -0,0 +1,57 @@ +"""Sensor platform support for yeelight.""" +import logging + +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.components.yeelight import DATA_YEELIGHT, DATA_UPDATED + +DEPENDENCIES = ['yeelight'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Yeelight sensors.""" + if not discovery_info: + return + + device = hass.data[DATA_YEELIGHT][discovery_info['host']] + + if device.is_nightlight_supported: + _LOGGER.debug("Adding nightlight mode sensor for %s", device.name) + add_entities([YeelightNightlightModeSensor(device)]) + + +class YeelightNightlightModeSensor(BinarySensorDevice): + """Representation of a Yeelight nightlight mode sensor.""" + + def __init__(self, device): + """Initialize nightlight mode sensor.""" + self._device = device + + @callback + def _schedule_immediate_update(self, ipaddr): + if ipaddr == self._device.ipaddr: + self.async_schedule_update_ha_state() + + async def async_added_to_hass(self): + """Handle entity which will be added.""" + async_dispatcher_connect( + self.hass, DATA_UPDATED, self._schedule_immediate_update + ) + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def name(self): + """Return the name of the sensor.""" + return "{} nightlight".format(self._device.name) + + @property + def is_on(self): + """Return true if nightlight mode is on.""" + return self._device.is_nightlight_enabled diff --git a/requirements_all.txt b/requirements_all.txt index 7fd0c906ed7fd7..f68146903098b8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1821,7 +1821,7 @@ yahooweather==0.10 yalesmartalarmclient==0.1.6 # homeassistant.components.yeelight -yeelight==0.4.3 +yeelight==0.4.4 # homeassistant.components.yeelightsunflower.light yeelightsunflower==0.0.10 From 6ffe9ad4736653507ed777a4de142cae29d36f04 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 25 Mar 2019 19:37:31 +0100 Subject: [PATCH 146/605] updated pydaikin (#22382) --- homeassistant/components/daikin/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 04cf8a584bf8cf..c757185a5cffe4 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,7 +17,7 @@ from . import config_flow # noqa pylint_disable=unused-import from .const import KEY_HOST -REQUIREMENTS = ['pydaikin==1.1.0'] +REQUIREMENTS = ['pydaikin==1.2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index f68146903098b8..1b954257e54d81 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -998,7 +998,7 @@ pycsspeechtts==1.0.2 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==1.1.0 +pydaikin==1.2.0 # homeassistant.components.danfoss_air pydanfossair==0.0.7 From 5ad3e75a4d4cb7a4100d9d718c5dea9c55797735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Maggioni?= Date: Mon, 25 Mar 2019 20:45:13 +0100 Subject: [PATCH 147/605] Support for Plex sensor with enforced SSL (#21432) --- homeassistant/components/plex/sensor.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index 46766d75010b18..eaf73ceb566ff9 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.switch import PLATFORM_SCHEMA from homeassistant.const import ( CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_HOST, CONF_PORT, CONF_TOKEN, - CONF_SSL) + CONF_SSL, CONF_VERIFY_SSL) from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv @@ -26,6 +26,7 @@ DEFAULT_NAME = 'Plex' DEFAULT_PORT = 32400 DEFAULT_SSL = False +DEFAULT_VERIFY_SSL = True MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) @@ -38,6 +39,7 @@ vol.Optional(CONF_SERVER): cv.string, vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, }) @@ -59,7 +61,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): try: add_entities([PlexSensor( name, plex_url, plex_user, plex_password, plex_server, - plex_token)], True) + plex_token, config.get(CONF_VERIFY_SSL))], True) except (plexapi.exceptions.BadRequest, plexapi.exceptions.Unauthorized, plexapi.exceptions.NotFound) as error: _LOGGER.error(error) @@ -70,23 +72,30 @@ class PlexSensor(Entity): """Representation of a Plex now playing sensor.""" def __init__(self, name, plex_url, plex_user, plex_password, - plex_server, plex_token): + plex_server, plex_token, verify_ssl): """Initialize the sensor.""" from plexapi.myplex import MyPlexAccount from plexapi.server import PlexServer + from requests import Session self._name = name self._state = 0 self._now_playing = [] + cert_session = None + if not verify_ssl: + _LOGGER.info("Ignoring SSL verification") + cert_session = Session() + cert_session.verify = False + if plex_token: - self._server = PlexServer(plex_url, plex_token) + self._server = PlexServer(plex_url, plex_token, cert_session) elif plex_user and plex_password: user = MyPlexAccount(plex_user, plex_password) server = plex_server if plex_server else user.resources()[0].name self._server = user.resource(server).connect() else: - self._server = PlexServer(plex_url) + self._server = PlexServer(plex_url, None, cert_session) @property def name(self): From 42c27e5b720862a861440cd171728cab7e208482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Marc=20Simard?= Date: Tue, 26 Mar 2019 00:51:49 -0400 Subject: [PATCH 148/605] Search GTFS departures across midnight (#20992) --- homeassistant/components/gtfs/sensor.py | 215 ++++++++++++++++++------ 1 file changed, 164 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 25b352c14545df..8eb5c623725afa 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -8,6 +8,7 @@ import logging import datetime import threading +from typing import Optional import voluptuous as vol @@ -25,6 +26,7 @@ CONF_DESTINATION = 'destination' CONF_ORIGIN = 'origin' CONF_OFFSET = 'offset' +CONF_TOMORROW = 'include_tomorrow' DEFAULT_NAME = 'GTFS Sensor' DEFAULT_PATH = 'gtfs' @@ -47,65 +49,162 @@ vol.Required(CONF_DATA): cv.string, vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_OFFSET, default=0): cv.time_period, + vol.Optional(CONF_TOMORROW, default=False): cv.boolean, }) -def get_next_departure(sched, start_station_id, end_station_id, offset): +def get_next_departure(sched, start_station_id, end_station_id, offset, + include_tomorrow=False) -> Optional[dict]: """Get the next departure for the given schedule.""" origin_station = sched.stops_by_id(start_station_id)[0] destination_station = sched.stops_by_id(end_station_id)[0] now = datetime.datetime.now() + offset - day_name = now.strftime('%A').lower() - now_str = now.strftime('%H:%M:%S') - today = now.strftime(dt_util.DATE_STR_FORMAT) + now_date = now.strftime(dt_util.DATE_STR_FORMAT) + yesterday = now - datetime.timedelta(days=1) + yesterday_date = yesterday.strftime(dt_util.DATE_STR_FORMAT) + tomorrow = now + datetime.timedelta(days=1) + tomorrow_date = tomorrow.strftime(dt_util.DATE_STR_FORMAT) from sqlalchemy.sql import text - sql_query = text(""" - SELECT trip.trip_id, trip.route_id, - time(origin_stop_time.arrival_time) AS origin_arrival_time, - time(origin_stop_time.departure_time) AS origin_depart_time, - origin_stop_time.drop_off_type AS origin_drop_off_type, - origin_stop_time.pickup_type AS origin_pickup_type, - origin_stop_time.shape_dist_traveled AS origin_dist_traveled, - origin_stop_time.stop_headsign AS origin_stop_headsign, - origin_stop_time.stop_sequence AS origin_stop_sequence, - time(destination_stop_time.arrival_time) AS dest_arrival_time, - time(destination_stop_time.departure_time) AS dest_depart_time, - destination_stop_time.drop_off_type AS dest_drop_off_type, - destination_stop_time.pickup_type AS dest_pickup_type, - destination_stop_time.shape_dist_traveled AS dest_dist_traveled, - destination_stop_time.stop_headsign AS dest_stop_headsign, - destination_stop_time.stop_sequence AS dest_stop_sequence - FROM trips trip - INNER JOIN calendar calendar - ON trip.service_id = calendar.service_id - INNER JOIN stop_times origin_stop_time - ON trip.trip_id = origin_stop_time.trip_id - INNER JOIN stops start_station - ON origin_stop_time.stop_id = start_station.stop_id - INNER JOIN stop_times destination_stop_time - ON trip.trip_id = destination_stop_time.trip_id - INNER JOIN stops end_station - ON destination_stop_time.stop_id = end_station.stop_id - WHERE calendar.{day_name} = 1 - AND origin_depart_time > time(:now_str) - AND start_station.stop_id = :origin_station_id - AND end_station.stop_id = :end_station_id - AND origin_stop_sequence < dest_stop_sequence - AND calendar.start_date <= :today - AND calendar.end_date >= :today - ORDER BY origin_stop_time.departure_time - LIMIT 1 - """.format(day_name=day_name)) - result = sched.engine.execute(sql_query, now_str=now_str, + # Fetch all departures for yesterday, today and optionally tomorrow, + # up to an overkill maximum in case of a departure every minute for those + # days. + limit = 24 * 60 * 60 * 2 + tomorrow_select = tomorrow_where = tomorrow_order = '' + if include_tomorrow: + limit = limit / 2 * 3 + tomorrow_name = tomorrow.strftime('%A').lower() + tomorrow_select = "calendar.{} AS tomorrow,".format(tomorrow_name) + tomorrow_where = "OR calendar.{} = 1".format(tomorrow_name) + tomorrow_order = "calendar.{} DESC,".format(tomorrow_name) + + sql_query = """ + SELECT trip.trip_id, trip.route_id, + time(origin_stop_time.arrival_time) AS origin_arrival_time, + time(origin_stop_time.departure_time) AS origin_depart_time, + date(origin_stop_time.departure_time) AS origin_departure_date, + origin_stop_time.drop_off_type AS origin_drop_off_type, + origin_stop_time.pickup_type AS origin_pickup_type, + origin_stop_time.shape_dist_traveled AS origin_dist_traveled, + origin_stop_time.stop_headsign AS origin_stop_headsign, + origin_stop_time.stop_sequence AS origin_stop_sequence, + time(destination_stop_time.arrival_time) AS dest_arrival_time, + time(destination_stop_time.departure_time) AS dest_depart_time, + destination_stop_time.drop_off_type AS dest_drop_off_type, + destination_stop_time.pickup_type AS dest_pickup_type, + destination_stop_time.shape_dist_traveled AS dest_dist_traveled, + destination_stop_time.stop_headsign AS dest_stop_headsign, + destination_stop_time.stop_sequence AS dest_stop_sequence, + calendar.{yesterday_name} AS yesterday, + calendar.{today_name} AS today, + {tomorrow_select} + calendar.start_date AS start_date, + calendar.end_date AS end_date + FROM trips trip + INNER JOIN calendar calendar + ON trip.service_id = calendar.service_id + INNER JOIN stop_times origin_stop_time + ON trip.trip_id = origin_stop_time.trip_id + INNER JOIN stops start_station + ON origin_stop_time.stop_id = start_station.stop_id + INNER JOIN stop_times destination_stop_time + ON trip.trip_id = destination_stop_time.trip_id + INNER JOIN stops end_station + ON destination_stop_time.stop_id = end_station.stop_id + WHERE (calendar.{yesterday_name} = 1 + OR calendar.{today_name} = 1 + {tomorrow_where} + ) + AND start_station.stop_id = :origin_station_id + AND end_station.stop_id = :end_station_id + AND origin_stop_sequence < dest_stop_sequence + AND calendar.start_date <= :today + AND calendar.end_date >= :today + ORDER BY calendar.{yesterday_name} DESC, + calendar.{today_name} DESC, + {tomorrow_order} + origin_stop_time.departure_time + LIMIT :limit + """.format(yesterday_name=yesterday.strftime('%A').lower(), + today_name=now.strftime('%A').lower(), + tomorrow_select=tomorrow_select, + tomorrow_where=tomorrow_where, + tomorrow_order=tomorrow_order) + result = sched.engine.execute(text(sql_query), origin_station_id=origin_station.id, end_station_id=destination_station.id, - today=today) - item = {} + today=now_date, + limit=limit) + + # Create lookup timetable for today and possibly tomorrow, taking into + # account any departures from yesterday scheduled after midnight, + # as long as all departures are within the calendar date range. + timetable = {} + yesterday_start = today_start = tomorrow_start = None + yesterday_last = today_last = None for row in result: - item = row + if row['yesterday'] == 1 and yesterday_date >= row['start_date']: + extras = { + 'day': 'yesterday', + 'first': None, + 'last': False, + } + if yesterday_start is None: + yesterday_start = row['origin_departure_date'] + if yesterday_start != row['origin_departure_date']: + idx = '{} {}'.format(now_date, + row['origin_depart_time']) + timetable[idx] = {**row, **extras} + yesterday_last = idx + + if row['today'] == 1: + extras = { + 'day': 'today', + 'first': False, + 'last': False, + } + if today_start is None: + today_start = row['origin_departure_date'] + extras['first'] = True + if today_start == row['origin_departure_date']: + idx_prefix = now_date + else: + idx_prefix = tomorrow_date + idx = '{} {}'.format(idx_prefix, row['origin_depart_time']) + timetable[idx] = {**row, **extras} + today_last = idx + + if 'tomorrow' in row and row['tomorrow'] == 1 and tomorrow_date <= \ + row['end_date']: + extras = { + 'day': 'tomorrow', + 'first': False, + 'last': None, + } + if tomorrow_start is None: + tomorrow_start = row['origin_departure_date'] + extras['first'] = True + if tomorrow_start == row['origin_departure_date']: + idx = '{} {}'.format(tomorrow_date, + row['origin_depart_time']) + timetable[idx] = {**row, **extras} + + # Flag last departures. + for idx in [yesterday_last, today_last]: + if idx is not None: + timetable[idx]['last'] = True + + _LOGGER.debug("Timetable: %s", sorted(timetable.keys())) + + item = {} + for key in sorted(timetable.keys()): + if dt_util.parse_datetime(key) > now: + item = timetable[key] + _LOGGER.debug("Departure found for station %s @ %s -> %s", + start_station_id, key, item) + break if item == {}: return None @@ -119,7 +218,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): origin_arrival.strftime(dt_util.DATE_STR_FORMAT), item['origin_arrival_time']) - origin_depart_time = '{} {}'.format(today, item['origin_depart_time']) + origin_depart_time = '{} {}'.format(now_date, item['origin_depart_time']) dest_arrival = now if item['dest_arrival_time'] < item['origin_depart_time']: @@ -162,6 +261,9 @@ def get_next_departure(sched, start_station_id, end_station_id, offset): return { 'trip_id': item['trip_id'], + 'day': item['day'], + 'first': item['first'], + 'last': item['last'], 'trip': sched.trips_by_id(item['trip_id'])[0], 'route': route, 'agency': sched.agencies_by_id(route.agency_id)[0], @@ -182,6 +284,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): destination = config.get(CONF_DESTINATION) name = config.get(CONF_NAME) offset = config.get(CONF_OFFSET) + include_tomorrow = config.get(CONF_TOMORROW) if not os.path.exists(gtfs_dir): os.makedirs(gtfs_dir) @@ -203,17 +306,20 @@ def setup_platform(hass, config, add_entities, discovery_info=None): pygtfs.append_feed(gtfs, os.path.join(gtfs_dir, data)) add_entities([ - GTFSDepartureSensor(gtfs, name, origin, destination, offset)]) + GTFSDepartureSensor(gtfs, name, origin, destination, offset, + include_tomorrow)]) class GTFSDepartureSensor(Entity): """Implementation of an GTFS departures sensor.""" - def __init__(self, pygtfs, name, origin, destination, offset): + def __init__(self, pygtfs, name, origin, destination, offset, + include_tomorrow) -> None: """Initialize the sensor.""" self._pygtfs = pygtfs self.origin = origin self.destination = destination + self._include_tomorrow = include_tomorrow self._offset = offset self._custom_name = name self._icon = ICON @@ -252,10 +358,13 @@ def update(self): """Get the latest data from GTFS and update the states.""" with self.lock: self._departure = get_next_departure( - self._pygtfs, self.origin, self.destination, self._offset) + self._pygtfs, self.origin, self.destination, self._offset, + self._include_tomorrow) if not self._departure: self._state = None - self._attributes = {'Info': 'No more departures today'} + self._attributes = {} + self._attributes['Info'] = "No more departures" if \ + self._include_tomorrow else "No more departures today" if self._name == '': self._name = (self._custom_name or DEFAULT_NAME) return @@ -284,8 +393,12 @@ def update(self): self._icon = ICONS.get(route.route_type, ICON) # Build attributes - self._attributes = {} self._attributes['arrival'] = arrival_time + self._attributes['day'] = self._departure['day'] + if self._departure['first'] is not None: + self._attributes['first'] = self._departure['first'] + if self._departure['last'] is not None: + self._attributes['last'] = self._departure['last'] self._attributes['offset'] = self._offset.seconds / 60 def dict_for_table(resource): From 0c4380a78d85be42a455afc444bf889308e41565 Mon Sep 17 00:00:00 2001 From: uchagani Date: Tue, 26 Mar 2019 01:36:39 -0400 Subject: [PATCH 149/605] remove config sections from hass.config.components (#22370) * remove config sections from hass.config.components * fix tests --- homeassistant/components/config/__init__.py | 1 - tests/components/config/test_init.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py index 0366dfa2b8bfeb..7807c52737091e 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -46,7 +46,6 @@ async def setup_panel(panel_name): if success: key = '{}.{}'.format(DOMAIN, panel_name) hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: key}) - hass.config.components.add(key) @callback def component_loaded(event): diff --git a/tests/components/config/test_init.py b/tests/components/config/test_init.py index 57ea7e7a492a40..41a0fb089b56d4 100644 --- a/tests/components/config/test_init.py +++ b/tests/components/config/test_init.py @@ -29,7 +29,6 @@ def test_load_on_demand_already_loaded(hass, aiohttp_client): yield from async_setup_component(hass, 'config', {}) yield from hass.async_block_till_done() - assert 'config.zwave' in hass.config.components assert stp.called @@ -47,5 +46,4 @@ def test_load_on_demand_on_load(hass, aiohttp_client): hass.bus.async_fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: 'zwave'}) yield from hass.async_block_till_done() - assert 'config.zwave' in hass.config.components assert stp.called From 8aef8c6bb425b56c21a7d81c1a54810674f86551 Mon Sep 17 00:00:00 2001 From: Daniel Shokouhi Date: Mon, 25 Mar 2019 23:37:59 -0700 Subject: [PATCH 150/605] Update ring_doorbell to 0.2.3 (#22395) --- homeassistant/components/ring/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 94f3be305fa699..74da7a9d542909 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -7,7 +7,7 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['ring_doorbell==0.2.2'] +REQUIREMENTS = ['ring_doorbell==0.2.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 1b954257e54d81..ff1a18d9f6d9f3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1520,7 +1520,7 @@ rfk101py==0.0.1 rflink==0.0.37 # homeassistant.components.ring -ring_doorbell==0.2.2 +ring_doorbell==0.2.3 # homeassistant.components.ritassist.device_tracker ritassist==0.9.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4bd50b713e2fc8..8f88e5bb67b94d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -276,7 +276,7 @@ restrictedpython==4.0b8 rflink==0.0.37 # homeassistant.components.ring -ring_doorbell==0.2.2 +ring_doorbell==0.2.3 # homeassistant.components.yamaha.media_player rxv==0.6.0 From b2ba9d07ca052bb142adfaefb052b67e74b7c5c0 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 26 Mar 2019 06:40:28 +0000 Subject: [PATCH 151/605] Fix unavailable state for homekit locks and covers (#22390) --- homeassistant/components/homekit_controller/cover.py | 10 ---------- homeassistant/components/homekit_controller/lock.py | 5 ----- 2 files changed, 15 deletions(-) diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index 4db1246b992ffe..7a4fa486ff9dff 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -82,11 +82,6 @@ def _update_door_state_current(self, value): def _update_obstruction_detected(self, value): self._obstruction_detected = value - @property - def available(self): - """Return True if entity is available.""" - return self._state is not None - @property def supported_features(self): """Flag supported features.""" @@ -146,11 +141,6 @@ def __init__(self, accessory, discovery_info): self._obstruction_detected = None self.lock_state = None - @property - def available(self): - """Return True if entity is available.""" - return self._state is not None - def get_characteristic_types(self): """Define the homekit characteristics the entity cares about.""" # pylint: disable=import-error diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index b084d7525d3b48..ac1bd8f88dacd0 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -64,11 +64,6 @@ def is_locked(self): """Return true if device is locked.""" return self._state == STATE_LOCKED - @property - def available(self): - """Return True if entity is available.""" - return self._state is not None - async def async_lock(self, **kwargs): """Lock the device.""" await self._set_lock_state(STATE_LOCKED) From 73b38572f0231ded1cb59e0d8cb7f4953bbf3e2e Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Tue, 26 Mar 2019 17:43:24 +1100 Subject: [PATCH 152/605] Add infer_arming_state option to ness alarm (#22379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add infer_arming_state option to ness alarm * actually use config value * 🤦‍♂️ --- homeassistant/components/ness_alarm/__init__.py | 11 +++++++++-- requirements_all.txt | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ness_alarm/__init__.py b/homeassistant/components/ness_alarm/__init__.py index 653ade806ecd97..4e8c8293c2d25a 100644 --- a/homeassistant/components/ness_alarm/__init__.py +++ b/homeassistant/components/ness_alarm/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['nessclient==0.9.14'] +REQUIREMENTS = ['nessclient==0.9.15'] _LOGGER = logging.getLogger(__name__) @@ -22,6 +22,7 @@ CONF_DEVICE_HOST = 'host' CONF_DEVICE_PORT = 'port' +CONF_INFER_ARMING_STATE = 'infer_arming_state' CONF_ZONES = 'zones' CONF_ZONE_NAME = 'name' CONF_ZONE_TYPE = 'type' @@ -29,6 +30,7 @@ ATTR_OUTPUT_ID = 'output_id' DEFAULT_ZONES = [] DEFAULT_SCAN_INTERVAL = datetime.timedelta(minutes=1) +DEFAULT_INFER_ARMING_STATE = False SIGNAL_ZONE_CHANGED = 'ness_alarm.zone_changed' SIGNAL_ARMING_STATE_CHANGED = 'ness_alarm.arming_state_changed' @@ -50,6 +52,9 @@ vol.All(cv.time_period, cv.positive_timedelta), vol.Optional(CONF_ZONES, default=DEFAULT_ZONES): vol.All(cv.ensure_list, [ZONE_SCHEMA]), + vol.Optional(CONF_INFER_ARMING_STATE, + default=DEFAULT_INFER_ARMING_STATE): + cv.boolean }), }, extra=vol.ALLOW_EXTRA) @@ -74,9 +79,11 @@ async def async_setup(hass, config): host = conf[CONF_DEVICE_HOST] port = conf[CONF_DEVICE_PORT] scan_interval = conf[CONF_SCAN_INTERVAL] + infer_arming_state = conf[CONF_INFER_ARMING_STATE] client = Client(host=host, port=port, loop=hass.loop, - update_interval=scan_interval.total_seconds()) + update_interval=scan_interval.total_seconds(), + infer_arming_state=infer_arming_state) hass.data[DATA_NESS] = client async def _close(event): diff --git a/requirements_all.txt b/requirements_all.txt index ff1a18d9f6d9f3..15db65c160833e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -735,7 +735,7 @@ nad_receiver==0.0.11 ndms2_client==0.0.6 # homeassistant.components.ness_alarm -nessclient==0.9.14 +nessclient==0.9.15 # homeassistant.components.netdata.sensor netdata==0.1.2 From 79445a7ccc82b994f05a11f14c38ca32badcbbaa Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 26 Mar 2019 07:43:58 +0100 Subject: [PATCH 153/605] deCONZ support Xiaomi vibration sensor (#22366) * Martin pointed out in previous PR that no ending '.' in logging * Add support for Xiaomi vibration sensor --- homeassistant/components/deconz/__init__.py | 2 +- homeassistant/components/deconz/binary_sensor.py | 10 +++++++++- homeassistant/components/deconz/gateway.py | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index 957bb5691108aa..8bdd946e2ef898 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,7 +12,7 @@ from .const import DEFAULT_PORT, DOMAIN, _LOGGER from .gateway import DeconzGateway -REQUIREMENTS = ['pydeconz==53'] +REQUIREMENTS = ['pydeconz==54'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index cb68b842f4af1e..2b0c2037248042 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -11,6 +11,10 @@ DEPENDENCIES = ['deconz'] +ATTR_ORIENTATION = 'orientation' +ATTR_TILTANGLE = 'tiltangle' +ATTR_VIBRATIONSTRENGTH = 'vibrationstrength' + async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): @@ -74,7 +78,7 @@ def icon(self): @property def device_state_attributes(self): """Return the state attributes of the sensor.""" - from pydeconz.sensor import PRESENCE + from pydeconz.sensor import PRESENCE, VIBRATION attr = {} if self._device.battery: attr[ATTR_BATTERY_LEVEL] = self._device.battery @@ -82,4 +86,8 @@ def device_state_attributes(self): attr[ATTR_ON] = self._device.on if self._device.type in PRESENCE and self._device.dark is not None: attr[ATTR_DARK] = self._device.dark + elif self._device.type in VIBRATION: + attr[ATTR_ORIENTATION] = self._device.orientation + attr[ATTR_TILTANGLE] = self._device.tiltangle + attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibrationstrength return attr diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 6629d4eec14557..11fb247a6f456e 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -44,7 +44,7 @@ async def async_setup(self): raise ConfigEntryNotReady except Exception: # pylint: disable=broad-except - _LOGGER.error('Error connecting with deCONZ gateway.') + _LOGGER.error('Error connecting with deCONZ gateway') return False for component in SUPPORTED_PLATFORMS: @@ -135,7 +135,7 @@ async def get_gateway(hass, config, async_add_device_callback, return deconz except errors.Unauthorized: - _LOGGER.warning("Invalid key for deCONZ at %s.", config[CONF_HOST]) + _LOGGER.warning("Invalid key for deCONZ at %s", config[CONF_HOST]) raise AuthenticationRequired except (asyncio.TimeoutError, errors.RequestError): diff --git a/requirements_all.txt b/requirements_all.txt index 15db65c160833e..401d22cd551371 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1004,7 +1004,7 @@ pydaikin==1.2.0 pydanfossair==0.0.7 # homeassistant.components.deconz -pydeconz==53 +pydeconz==54 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8f88e5bb67b94d..17c993bf5f7312 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -203,7 +203,7 @@ pyHS100==0.3.4 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==53 +pydeconz==54 # homeassistant.components.zwave pydispatcher==2.0.5 From 6fa8fdf5558d21dc5ca7e718d141c6549f5e1a97 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Tue, 26 Mar 2019 07:46:00 +0100 Subject: [PATCH 154/605] Fix data_key of the xiaomi_aqara cover for LAN protocol v2 (#22358) --- homeassistant/components/xiaomi_aqara/cover.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/xiaomi_aqara/cover.py b/homeassistant/components/xiaomi_aqara/cover.py index f4bf1f269b5cfc..cd9190dca351f6 100644 --- a/homeassistant/components/xiaomi_aqara/cover.py +++ b/homeassistant/components/xiaomi_aqara/cover.py @@ -17,8 +17,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): for device in gateway.devices['cover']: model = device['model'] if model == 'curtain': + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key = 'status' + else: + data_key = 'curtain_status' devices.append(XiaomiGenericCover(device, "Curtain", - 'status', gateway)) + data_key, gateway)) add_entities(devices) From a62c1169592526f09e32248ccc45c3eeffc78ef5 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 26 Mar 2019 06:49:51 +0000 Subject: [PATCH 155/605] Remove get_serial helper that is no longer needed. (#22368) --- .../components/homekit_controller/__init__.py | 38 ++++++------------- .../homekit_controller/alarm_control_panel.py | 4 +- .../homekit_controller/binary_sensor.py | 4 +- .../components/homekit_controller/climate.py | 4 +- .../components/homekit_controller/const.py | 1 - .../components/homekit_controller/cover.py | 4 +- .../components/homekit_controller/light.py | 4 +- .../components/homekit_controller/lock.py | 4 +- .../components/homekit_controller/sensor.py | 4 +- .../components/homekit_controller/switch.py | 4 +- 10 files changed, 27 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 0cb9ecbfc0789b..5e470e1540baa6 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -11,8 +11,7 @@ from .connection import get_accessory_information from .const import ( - CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_ACCESSORIES, - KNOWN_DEVICES + CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_DEVICES ) @@ -33,25 +32,6 @@ PAIRING_FILE = "pairing.json" -def get_serial(accessory): - """Obtain the serial number of a HomeKit device.""" - # pylint: disable=import-error - from homekit.model.services import ServicesTypes - from homekit.model.characteristics import CharacteristicsTypes - - for service in accessory['services']: - if ServicesTypes.get_short(service['type']) != \ - 'accessory-information': - continue - for characteristic in service['characteristics']: - ctype = CharacteristicsTypes.get_short( - characteristic['type']) - if ctype != 'serial-number': - continue - return characteristic['value'] - return None - - def escape_characteristic_name(char_name): """Escape any dash or dots in a characteristics name.""" return char_name.replace('-', '_').replace('.', '_') @@ -75,6 +55,10 @@ def __init__(self, hass, host, port, model, hkid, config_num, config): self.configurator = hass.components.configurator self._connection_warning_logged = False + # This just tracks aid/iid pairs so we know if a HK service has been + # mapped to a HA entity. + self.entities = [] + self.pairing_lock = asyncio.Lock(loop=hass.loop) self.pairing = self.controller.pairings.get(hkid) @@ -100,15 +84,16 @@ def accessory_setup(self): self.hass, RETRY_INTERVAL, lambda _: self.accessory_setup()) return for accessory in data: - serial = get_serial(accessory) - if serial in self.hass.data[KNOWN_ACCESSORIES]: - continue - self.hass.data[KNOWN_ACCESSORIES][serial] = self aid = accessory['aid'] for service in accessory['services']: + iid = service['iid'] + if (aid, iid) in self.entities: + # Don't add the same entity again + continue + devtype = ServicesTypes.get_short(service['type']) _LOGGER.debug("Found %s", devtype) - service_info = {'serial': serial, + service_info = {'serial': self.hkid, 'aid': aid, 'iid': service['iid'], 'model': self.model, @@ -381,7 +366,6 @@ def discovery_dispatch(service, discovery_info): device = HKDevice(hass, host, port, model, hkid, config_num, config) hass.data[KNOWN_DEVICES][hkid] = device - hass.data[KNOWN_ACCESSORIES] = {} hass.data[KNOWN_DEVICES] = {} discovery.listen(hass, SERVICE_HOMEKIT, discovery_dispatch) return True diff --git a/homeassistant/components/homekit_controller/alarm_control_panel.py b/homeassistant/components/homekit_controller/alarm_control_panel.py index 9bc15aad75dcb0..f9bc25f4237e0c 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -6,7 +6,7 @@ ATTR_BATTERY_LEVEL, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -34,7 +34,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit Alarm Control Panel support.""" if discovery_info is None: return - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitAlarmControlPanel(accessory, discovery_info)], True) diff --git a/homeassistant/components/homekit_controller/binary_sensor.py b/homeassistant/components/homekit_controller/binary_sensor.py index 7fcc5b4e833231..2bd03b18932146 100644 --- a/homeassistant/components/homekit_controller/binary_sensor.py +++ b/homeassistant/components/homekit_controller/binary_sensor.py @@ -3,7 +3,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -13,7 +13,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit motion sensor support.""" if discovery_info is not None: - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitMotionSensor(accessory, discovery_info)], True) diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 243b795e792fd9..67f1fb72bcfbb5 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -7,7 +7,7 @@ SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -29,7 +29,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit climate.""" if discovery_info is not None: - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitClimateDevice(accessory, discovery_info)], True) diff --git a/homeassistant/components/homekit_controller/const.py b/homeassistant/components/homekit_controller/const.py index 873f6b343d21f8..90a105b0ad9c41 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -1,7 +1,6 @@ """Constants for the homekit_controller component.""" DOMAIN = 'homekit_controller' -KNOWN_ACCESSORIES = "{}-accessories".format(DOMAIN) KNOWN_DEVICES = "{}-devices".format(DOMAIN) CONTROLLER = "{}-controller".format(DOMAIN) diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index 7a4fa486ff9dff..26b7613ed2b6d5 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -8,7 +8,7 @@ from homeassistant.const import ( STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING) -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity STATE_STOPPED = 'stopped' @@ -41,7 +41,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up HomeKit Cover support.""" if discovery_info is None: return - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] if discovery_info['device-type'] == 'garage-door-opener': add_entities([HomeKitGarageDoorCover(accessory, discovery_info)], diff --git a/homeassistant/components/homekit_controller/light.py b/homeassistant/components/homekit_controller/light.py index db8fd332c0cf97..cb9259df4a992d 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -5,7 +5,7 @@ ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, Light) -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -15,7 +15,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit lighting.""" if discovery_info is not None: - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitLight(accessory, discovery_info)], True) diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index ac1bd8f88dacd0..0d0275fda164e7 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -5,7 +5,7 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_LOCKED, STATE_UNLOCKED) -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -30,7 +30,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit Lock support.""" if discovery_info is None: return - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitLock(accessory, discovery_info)], True) diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 955a1a7927e0e5..8cbc8f248bafe0 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -1,7 +1,7 @@ """Support for Homekit sensors.""" from homeassistant.const import TEMP_CELSIUS -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -16,7 +16,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit sensor support.""" if discovery_info is not None: - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] devtype = discovery_info['device-type'] if devtype == 'humidity': diff --git a/homeassistant/components/homekit_controller/switch.py b/homeassistant/components/homekit_controller/switch.py index ba19413d4115c6..34e83c06526758 100644 --- a/homeassistant/components/homekit_controller/switch.py +++ b/homeassistant/components/homekit_controller/switch.py @@ -3,7 +3,7 @@ from homeassistant.components.switch import SwitchDevice -from . import KNOWN_ACCESSORIES, HomeKitEntity +from . import KNOWN_DEVICES, HomeKitEntity DEPENDENCIES = ['homekit_controller'] @@ -15,7 +15,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Homekit switch support.""" if discovery_info is not None: - accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] + accessory = hass.data[KNOWN_DEVICES][discovery_info['serial']] add_entities([HomeKitSwitch(accessory, discovery_info)], True) From e85b089effbf584af3ca10eb118dd613f8509f27 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 25 Mar 2019 23:53:36 -0700 Subject: [PATCH 156/605] Set default parallel_update value should base on async_update (#22149) * Set default parallel_update value should base on async_update * Set default parallel_update value should base on async_update * Delay the parallel_update_semaphore creation * Remove outdated comment --- homeassistant/helpers/entity_platform.py | 43 +++-- tests/helpers/test_entity.py | 226 +++++++++++++++-------- tests/helpers/test_entity_platform.py | 98 +++++++--- 3 files changed, 244 insertions(+), 123 deletions(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 87cc4d4fd90edf..a092c89405e742 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -27,7 +27,6 @@ def __init__(self, *, hass, logger, domain, platform_name, platform, domain: str platform_name: str scan_interval: timedelta - parallel_updates: int entity_namespace: str async_entities_added_callback: @callback method """ @@ -52,22 +51,21 @@ def __init__(self, *, hass, logger, domain, platform_name, platform, # which powers entity_component.add_entities if platform is None: self.parallel_updates = None + self.parallel_updates_semaphore = None return - # Async platforms do all updates in parallel by default - if hasattr(platform, 'async_setup_platform'): - default_parallel_updates = 0 - else: - default_parallel_updates = 1 - - parallel_updates = getattr(platform, 'PARALLEL_UPDATES', - default_parallel_updates) + self.parallel_updates = getattr(platform, 'PARALLEL_UPDATES', None) + # semaphore will be created on demand + self.parallel_updates_semaphore = None - if parallel_updates: - self.parallel_updates = asyncio.Semaphore( - parallel_updates, loop=hass.loop) - else: - self.parallel_updates = None + def _get_parallel_updates_semaphore(self): + """Get or create a semaphore for parallel updates.""" + if self.parallel_updates_semaphore is None: + self.parallel_updates_semaphore = asyncio.Semaphore( + self.parallel_updates if self.parallel_updates else 1, + loop=self.hass.loop + ) + return self.parallel_updates_semaphore async def async_setup(self, platform_config, discovery_info=None): """Set up the platform from a config file.""" @@ -240,7 +238,22 @@ async def _async_add_entity(self, entity, update_before_add, entity.hass = self.hass entity.platform = self - entity.parallel_updates = self.parallel_updates + + # Async entity + # PARALLEL_UPDATE == None: entity.parallel_updates = None + # PARALLEL_UPDATE == 0: entity.parallel_updates = None + # PARALLEL_UPDATE > 0: entity.parallel_updates = Semaphore(p) + # Sync entity + # PARALLEL_UPDATE == None: entity.parallel_updates = Semaphore(1) + # PARALLEL_UPDATE == 0: entity.parallel_updates = None + # PARALLEL_UPDATE > 0: entity.parallel_updates = Semaphore(p) + if hasattr(entity, 'async_update') and not self.parallel_updates: + entity.parallel_updates = None + elif (not hasattr(entity, 'async_update') + and self.parallel_updates == 0): + entity.parallel_updates = None + else: + entity.parallel_updates = self._get_parallel_updates_semaphore() # Update properties before we generate the entity_id if update_before_add: diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index d79f84d416d268..383cd05a009754 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -1,6 +1,7 @@ """Test the entity helper.""" # pylint: disable=protected-access import asyncio +import threading from datetime import timedelta from unittest.mock import MagicMock, patch, PropertyMock @@ -225,11 +226,10 @@ def async_update(): assert update_call is True -@asyncio.coroutine -def test_async_parallel_updates_with_zero(hass): +async def test_async_parallel_updates_with_zero(hass): """Test parallel updates with 0 (disabled).""" updates = [] - test_lock = asyncio.Event(loop=hass.loop) + test_lock = asyncio.Event() class AsyncEntity(entity.Entity): @@ -239,37 +239,73 @@ def __init__(self, entity_id, count): self.hass = hass self._count = count - @asyncio.coroutine - def async_update(self): + async def async_update(self): """Test update.""" updates.append(self._count) - yield from test_lock.wait() + await test_lock.wait() ent_1 = AsyncEntity("sensor.test_1", 1) ent_2 = AsyncEntity("sensor.test_2", 2) - ent_1.async_schedule_update_ha_state(True) - ent_2.async_schedule_update_ha_state(True) + try: + ent_1.async_schedule_update_ha_state(True) + ent_2.async_schedule_update_ha_state(True) - while True: - if len(updates) == 2: - break - yield from asyncio.sleep(0, loop=hass.loop) + while True: + if len(updates) >= 2: + break + await asyncio.sleep(0) - assert len(updates) == 2 - assert updates == [1, 2] + assert len(updates) == 2 + assert updates == [1, 2] + finally: + test_lock.set() - test_lock.set() +async def test_async_parallel_updates_with_zero_on_sync_update(hass): + """Test parallel updates with 0 (disabled).""" + updates = [] + test_lock = threading.Event() + + class AsyncEntity(entity.Entity): -@asyncio.coroutine -def test_async_parallel_updates_with_one(hass): + def __init__(self, entity_id, count): + """Initialize Async test entity.""" + self.entity_id = entity_id + self.hass = hass + self._count = count + + def update(self): + """Test update.""" + updates.append(self._count) + if not test_lock.wait(timeout=1): + # if timeout populate more data to fail the test + updates.append(self._count) + + ent_1 = AsyncEntity("sensor.test_1", 1) + ent_2 = AsyncEntity("sensor.test_2", 2) + + try: + ent_1.async_schedule_update_ha_state(True) + ent_2.async_schedule_update_ha_state(True) + + while True: + if len(updates) >= 2: + break + await asyncio.sleep(0) + + assert len(updates) == 2 + assert updates == [1, 2] + finally: + test_lock.set() + await asyncio.sleep(0) + + +async def test_async_parallel_updates_with_one(hass): """Test parallel updates with 1 (sequential).""" updates = [] - test_lock = asyncio.Lock(loop=hass.loop) - test_semaphore = asyncio.Semaphore(1, loop=hass.loop) - - yield from test_lock.acquire() + test_lock = asyncio.Lock() + test_semaphore = asyncio.Semaphore(1) class AsyncEntity(entity.Entity): @@ -280,59 +316,71 @@ def __init__(self, entity_id, count): self._count = count self.parallel_updates = test_semaphore - @asyncio.coroutine - def async_update(self): + async def async_update(self): """Test update.""" updates.append(self._count) - yield from test_lock.acquire() + await test_lock.acquire() ent_1 = AsyncEntity("sensor.test_1", 1) ent_2 = AsyncEntity("sensor.test_2", 2) ent_3 = AsyncEntity("sensor.test_3", 3) - ent_1.async_schedule_update_ha_state(True) - ent_2.async_schedule_update_ha_state(True) - ent_3.async_schedule_update_ha_state(True) + await test_lock.acquire() - while True: - if len(updates) == 1: - break - yield from asyncio.sleep(0, loop=hass.loop) + try: + ent_1.async_schedule_update_ha_state(True) + ent_2.async_schedule_update_ha_state(True) + ent_3.async_schedule_update_ha_state(True) - assert len(updates) == 1 - assert updates == [1] + while True: + if len(updates) >= 1: + break + await asyncio.sleep(0) - test_lock.release() + assert len(updates) == 1 + assert updates == [1] - while True: - if len(updates) == 2: - break - yield from asyncio.sleep(0, loop=hass.loop) + updates.clear() + test_lock.release() + await asyncio.sleep(0) - assert len(updates) == 2 - assert updates == [1, 2] + while True: + if len(updates) >= 1: + break + await asyncio.sleep(0) - test_lock.release() + assert len(updates) == 1 + assert updates == [2] - while True: - if len(updates) == 3: - break - yield from asyncio.sleep(0, loop=hass.loop) + updates.clear() + test_lock.release() + await asyncio.sleep(0) - assert len(updates) == 3 - assert updates == [1, 2, 3] + while True: + if len(updates) >= 1: + break + await asyncio.sleep(0) - test_lock.release() + assert len(updates) == 1 + assert updates == [3] + updates.clear() + test_lock.release() + await asyncio.sleep(0) -@asyncio.coroutine -def test_async_parallel_updates_with_two(hass): + finally: + # we may have more than one lock need to release in case test failed + for _ in updates: + test_lock.release() + await asyncio.sleep(0) + test_lock.release() + + +async def test_async_parallel_updates_with_two(hass): """Test parallel updates with 2 (parallel).""" updates = [] - test_lock = asyncio.Lock(loop=hass.loop) - test_semaphore = asyncio.Semaphore(2, loop=hass.loop) - - yield from test_lock.acquire() + test_lock = asyncio.Lock() + test_semaphore = asyncio.Semaphore(2) class AsyncEntity(entity.Entity): @@ -354,34 +402,48 @@ def async_update(self): ent_3 = AsyncEntity("sensor.test_3", 3) ent_4 = AsyncEntity("sensor.test_4", 4) - ent_1.async_schedule_update_ha_state(True) - ent_2.async_schedule_update_ha_state(True) - ent_3.async_schedule_update_ha_state(True) - ent_4.async_schedule_update_ha_state(True) - - while True: - if len(updates) == 2: - break - yield from asyncio.sleep(0, loop=hass.loop) - - assert len(updates) == 2 - assert updates == [1, 2] - - test_lock.release() - yield from asyncio.sleep(0, loop=hass.loop) - test_lock.release() - - while True: - if len(updates) == 4: - break - yield from asyncio.sleep(0, loop=hass.loop) - - assert len(updates) == 4 - assert updates == [1, 2, 3, 4] - - test_lock.release() - yield from asyncio.sleep(0, loop=hass.loop) - test_lock.release() + await test_lock.acquire() + + try: + + ent_1.async_schedule_update_ha_state(True) + ent_2.async_schedule_update_ha_state(True) + ent_3.async_schedule_update_ha_state(True) + ent_4.async_schedule_update_ha_state(True) + + while True: + if len(updates) >= 2: + break + await asyncio.sleep(0) + + assert len(updates) == 2 + assert updates == [1, 2] + + updates.clear() + test_lock.release() + await asyncio.sleep(0) + test_lock.release() + await asyncio.sleep(0) + + while True: + if len(updates) >= 2: + break + await asyncio.sleep(0) + + assert len(updates) == 2 + assert updates == [3, 4] + + updates.clear() + test_lock.release() + await asyncio.sleep(0) + test_lock.release() + await asyncio.sleep(0) + finally: + # we may have more than one lock need to release in case test failed + for _ in updates: + test_lock.release() + await asyncio.sleep(0) + test_lock.release() @asyncio.coroutine diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index e985771e486b61..6cf0bb0eeeb89c 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -251,23 +251,46 @@ def async_update(self): assert entity_ids[0] == "test_domain.living_room" -@asyncio.coroutine -def test_parallel_updates_async_platform(hass): - """Warn we log when platform setup takes a long time.""" +async def test_parallel_updates_async_platform(hass): + """Test async platform does not have parallel_updates limit by default.""" platform = MockPlatform() - @asyncio.coroutine - def mock_update(*args, **kwargs): - pass + loader.set_component(hass, 'test_domain.platform', platform) + + component = EntityComponent(_LOGGER, DOMAIN, hass) + component._platforms = {} + + await component.async_setup({ + DOMAIN: { + 'platform': 'platform', + } + }) + + handle = list(component._platforms.values())[-1] + assert handle.parallel_updates is None + + class AsyncEntity(MockEntity): + """Mock entity that has async_update.""" + + async def async_update(self): + pass + + entity = AsyncEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is None - platform.async_setup_platform = mock_update + +async def test_parallel_updates_async_platform_with_constant(hass): + """Test async platform can set parallel_updates limit.""" + platform = MockPlatform() + platform.PARALLEL_UPDATES = 2 loader.set_component(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} - yield from component.async_setup({ + await component.async_setup({ DOMAIN: { 'platform': 'platform', } @@ -275,56 +298,79 @@ def mock_update(*args, **kwargs): handle = list(component._platforms.values())[-1] - assert handle.parallel_updates is None + assert handle.parallel_updates == 2 + class AsyncEntity(MockEntity): + """Mock entity that has async_update.""" -@asyncio.coroutine -def test_parallel_updates_async_platform_with_constant(hass): - """Warn we log when platform setup takes a long time.""" - platform = MockPlatform() + async def async_update(self): + pass - @asyncio.coroutine - def mock_update(*args, **kwargs): - pass + entity = AsyncEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is not None + assert entity.parallel_updates._value == 2 - platform.async_setup_platform = mock_update - platform.PARALLEL_UPDATES = 1 + +async def test_parallel_updates_sync_platform(hass): + """Test sync platform parallel_updates default set to 1.""" + platform = MockPlatform() loader.set_component(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} - yield from component.async_setup({ + await component.async_setup({ DOMAIN: { 'platform': 'platform', } }) handle = list(component._platforms.values())[-1] + assert handle.parallel_updates is None - assert handle.parallel_updates is not None + class SyncEntity(MockEntity): + """Mock entity that has update.""" + async def update(self): + pass + + entity = SyncEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is not None + assert entity.parallel_updates._value == 1 -@asyncio.coroutine -def test_parallel_updates_sync_platform(hass): - """Warn we log when platform setup takes a long time.""" - platform = MockPlatform(setup_platform=lambda *args: None) + +async def test_parallel_updates_sync_platform_with_constant(hass): + """Test sync platform can set parallel_updates limit.""" + platform = MockPlatform() + platform.PARALLEL_UPDATES = 2 loader.set_component(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} - yield from component.async_setup({ + await component.async_setup({ DOMAIN: { 'platform': 'platform', } }) handle = list(component._platforms.values())[-1] + assert handle.parallel_updates == 2 + + class SyncEntity(MockEntity): + """Mock entity that has update.""" + + async def update(self): + pass - assert handle.parallel_updates is not None + entity = SyncEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is not None + assert entity.parallel_updates._value == 2 @asyncio.coroutine From baa4945944c2221212d123536ad6a7831b0b2d41 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 26 Mar 2019 03:39:09 -0400 Subject: [PATCH 157/605] reset unsub to None on timeout (#22404) --- homeassistant/components/stream/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 665803d38ebcab..59c0a6b650fd46 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -128,6 +128,7 @@ def put(self, segment: Segment) -> None: @callback def _timeout(self, _now=None): """Handle stream timeout.""" + self._unsub = None if self._stream.keepalive: self.idle = True self._stream.check_idle() From bad0a8b342cb0b5a722ef6a357247009f58e4c9d Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 26 Mar 2019 08:31:29 -0400 Subject: [PATCH 158/605] Camera Preferences + Preload Stream (#22339) * initial commit for camera preferences and preload stream * cleanup and add tests * respect camera preferences on each request stream call * return the new prefs after update --- homeassistant/components/camera/__init__.py | 68 +++++++++-- homeassistant/components/camera/const.py | 6 + homeassistant/components/camera/prefs.py | 60 ++++++++++ homeassistant/components/local_file/camera.py | 3 +- .../components/logi_circle/camera.py | 3 +- homeassistant/components/onvif/camera.py | 3 +- homeassistant/components/push/camera.py | 3 +- homeassistant/components/stream/__init__.py | 3 + tests/components/camera/common.py | 14 ++- tests/components/camera/test_init.py | 109 ++++++++++++++++-- tests/components/local_file/test_camera.py | 2 +- 11 files changed, 252 insertions(+), 22 deletions(-) create mode 100644 homeassistant/components/camera/const.py create mode 100644 homeassistant/components/camera/prefs.py diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 046b6d3947c064..cdd8a844389a54 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -20,7 +20,7 @@ from homeassistant.core import callback from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, \ - SERVICE_TURN_ON + SERVICE_TURN_ON, EVENT_HOMEASSISTANT_START from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import bind_hass from homeassistant.helpers.entity import Entity @@ -37,7 +37,9 @@ from homeassistant.components import websocket_api import homeassistant.helpers.config_validation as cv -DOMAIN = 'camera' +from .const import DOMAIN, DATA_CAMERA_PREFS +from .prefs import CameraPreferences + DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) @@ -68,7 +70,6 @@ TOKEN_CHANGE_INTERVAL = timedelta(minutes=5) _RND = SystemRandom() -FALLBACK_STREAM_INTERVAL = 1 # seconds MIN_STREAM_INTERVAL = 0.5 # seconds CAMERA_SERVICE_SCHEMA = vol.Schema({ @@ -103,12 +104,14 @@ class Image: async def async_request_stream(hass, entity_id, fmt): """Request a stream for a camera entity.""" camera = _get_camera_from_entity_id(hass, entity_id) + camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id) if not camera.stream_source: raise HomeAssistantError("{} does not support play stream service" .format(camera.entity_id)) - return request_stream(hass, camera.stream_source, fmt=fmt) + return request_stream(hass, camera.stream_source, fmt=fmt, + keepalive=camera_prefs.preload_stream) @bind_hass @@ -197,6 +200,10 @@ async def async_setup(hass, config): component = hass.data[DOMAIN] = \ EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) + prefs = CameraPreferences(hass) + await prefs.async_initialize() + hass.data[DATA_CAMERA_PREFS] = prefs + hass.http.register_view(CameraImageView(component)) hass.http.register_view(CameraMjpegStream(component)) hass.components.websocket_api.async_register_command( @@ -204,9 +211,21 @@ async def async_setup(hass, config): SCHEMA_WS_CAMERA_THUMBNAIL ) hass.components.websocket_api.async_register_command(ws_camera_stream) + hass.components.websocket_api.async_register_command(websocket_get_prefs) + hass.components.websocket_api.async_register_command( + websocket_update_prefs) await component.async_setup(config) + @callback + def preload_stream(event): + for camera in component.entities: + camera_prefs = prefs.get(camera.entity_id) + if camera.stream_source and camera_prefs.preload_stream: + request_stream(hass, camera.stream_source, keepalive=True) + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, preload_stream) + @callback def update_tokens(time): """Update tokens of the entities.""" @@ -522,14 +541,17 @@ async def ws_camera_stream(hass, connection, msg): Async friendly. """ try: - camera = _get_camera_from_entity_id(hass, msg['entity_id']) + entity_id = msg['entity_id'] + camera = _get_camera_from_entity_id(hass, entity_id) + camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id) if not camera.stream_source: raise HomeAssistantError("{} does not support play stream service" .format(camera.entity_id)) fmt = msg['format'] - url = request_stream(hass, camera.stream_source, fmt=fmt) + url = request_stream(hass, camera.stream_source, fmt=fmt, + keepalive=camera_prefs.preload_stream) connection.send_result(msg['id'], {'url': url}) except HomeAssistantError as ex: _LOGGER.error(ex) @@ -537,6 +559,36 @@ async def ws_camera_stream(hass, connection, msg): msg['id'], 'start_stream_failed', str(ex)) +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required('type'): 'camera/get_prefs', + vol.Required('entity_id'): cv.entity_id, +}) +async def websocket_get_prefs(hass, connection, msg): + """Handle request for account info.""" + prefs = hass.data[DATA_CAMERA_PREFS].get(msg['entity_id']) + connection.send_result(msg['id'], prefs.as_dict()) + + +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required('type'): 'camera/update_prefs', + vol.Required('entity_id'): cv.entity_id, + vol.Optional('preload_stream'): bool, +}) +async def websocket_update_prefs(hass, connection, msg): + """Handle request for account info.""" + prefs = hass.data[DATA_CAMERA_PREFS] + + changes = dict(msg) + changes.pop('id') + changes.pop('type') + entity_id = changes.pop('entity_id') + await prefs.async_update(entity_id, **changes) + + connection.send_result(msg['id'], prefs.get(entity_id).as_dict()) + + async def async_handle_snapshot_service(camera, service): """Handle snapshot services calls.""" hass = camera.hass @@ -573,10 +625,12 @@ async def async_handle_play_stream_service(camera, service_call): .format(camera.entity_id)) hass = camera.hass + camera_prefs = hass.data[DATA_CAMERA_PREFS].get(camera.entity_id) fmt = service_call.data[ATTR_FORMAT] entity_ids = service_call.data[ATTR_MEDIA_PLAYER] - url = request_stream(hass, camera.stream_source, fmt=fmt) + url = request_stream(hass, camera.stream_source, fmt=fmt, + keepalive=camera_prefs.preload_stream) data = { ATTR_ENTITY_ID: entity_ids, ATTR_MEDIA_CONTENT_ID: "{}{}".format(hass.config.api.base_url, url), diff --git a/homeassistant/components/camera/const.py b/homeassistant/components/camera/const.py new file mode 100644 index 00000000000000..f87ca47460e644 --- /dev/null +++ b/homeassistant/components/camera/const.py @@ -0,0 +1,6 @@ +"""Constants for Camera component.""" +DOMAIN = 'camera' + +DATA_CAMERA_PREFS = 'camera_prefs' + +PREF_PRELOAD_STREAM = 'preload_stream' diff --git a/homeassistant/components/camera/prefs.py b/homeassistant/components/camera/prefs.py new file mode 100644 index 00000000000000..927929bdf6eef0 --- /dev/null +++ b/homeassistant/components/camera/prefs.py @@ -0,0 +1,60 @@ +"""Preference management for camera component.""" +from .const import DOMAIN, PREF_PRELOAD_STREAM + +STORAGE_KEY = DOMAIN +STORAGE_VERSION = 1 +_UNDEF = object() + + +class CameraEntityPreferences: + """Handle preferences for camera entity.""" + + def __init__(self, prefs): + """Initialize prefs.""" + self._prefs = prefs + + def as_dict(self): + """Return dictionary version.""" + return self._prefs + + @property + def preload_stream(self): + """Return if stream is loaded on hass start.""" + return self._prefs.get(PREF_PRELOAD_STREAM, False) + + +class CameraPreferences: + """Handle camera preferences.""" + + def __init__(self, hass): + """Initialize camera prefs.""" + self._hass = hass + self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._prefs = None + + async def async_initialize(self): + """Finish initializing the preferences.""" + prefs = await self._store.async_load() + + if prefs is None: + prefs = {} + + self._prefs = prefs + + async def async_update(self, entity_id, *, preload_stream=_UNDEF, + stream_options=_UNDEF): + """Update camera preferences.""" + if not self._prefs.get(entity_id): + self._prefs[entity_id] = {} + + for key, value in ( + (PREF_PRELOAD_STREAM, preload_stream), + ): + if value is not _UNDEF: + self._prefs[entity_id][key] = value + + await self._store.async_save(self._prefs) + + def get(self, entity_id): + """Get preferences for an entity.""" + return CameraEntityPreferences(self._prefs.get(entity_id, {})) diff --git a/homeassistant/components/local_file/camera.py b/homeassistant/components/local_file/camera.py index d306509b76258a..56780d16f5683a 100644 --- a/homeassistant/components/local_file/camera.py +++ b/homeassistant/components/local_file/camera.py @@ -12,7 +12,8 @@ from homeassistant.const import CONF_NAME from homeassistant.components.camera import ( - Camera, CAMERA_SERVICE_SCHEMA, DOMAIN, PLATFORM_SCHEMA) + Camera, CAMERA_SERVICE_SCHEMA, PLATFORM_SCHEMA) +from homeassistant.components.camera.const import DOMAIN from homeassistant.helpers import config_validation as cv _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/logi_circle/camera.py b/homeassistant/components/logi_circle/camera.py index 814475d04de916..ff6f14431d5819 100644 --- a/homeassistant/components/logi_circle/camera.py +++ b/homeassistant/components/logi_circle/camera.py @@ -6,8 +6,9 @@ import voluptuous as vol from homeassistant.components.camera import ( - ATTR_ENTITY_ID, ATTR_FILENAME, CAMERA_SERVICE_SCHEMA, DOMAIN, + ATTR_ENTITY_ID, ATTR_FILENAME, CAMERA_SERVICE_SCHEMA, PLATFORM_SCHEMA, SUPPORT_ON_OFF, Camera) +from homeassistant.components.camera.const import DOMAIN from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, CONF_SCAN_INTERVAL, STATE_OFF, STATE_ON) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index da0bae7c50bacb..f3b25e3a1285a5 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -13,7 +13,8 @@ from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, ATTR_ENTITY_ID) -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA, DOMAIN +from homeassistant.components.camera import Camera, PLATFORM_SCHEMA +from homeassistant.components.camera.const import DOMAIN from homeassistant.components.ffmpeg import ( DATA_FFMPEG, CONF_EXTRA_ARGUMENTS) import homeassistant.helpers.config_validation as cv diff --git a/homeassistant/components/push/camera.py b/homeassistant/components/push/camera.py index 36c4a3109baba2..5490cd1508cbd8 100644 --- a/homeassistant/components/push/camera.py +++ b/homeassistant/components/push/camera.py @@ -14,7 +14,8 @@ import async_timeout from homeassistant.components.camera import Camera, PLATFORM_SCHEMA,\ - STATE_IDLE, STATE_RECORDING, DOMAIN + STATE_IDLE, STATE_RECORDING +from homeassistant.components.camera.const import DOMAIN from homeassistant.core import callback from homeassistant.const import CONF_NAME, CONF_TIMEOUT, CONF_WEBHOOK_ID from homeassistant.helpers import config_validation as cv diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index c881ec1276a96f..a68f1c47dbf413 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -56,6 +56,9 @@ def request_stream(hass, stream_source, *, fmt='hls', stream = Stream(hass, stream_source, options=options, keepalive=keepalive) streams[stream_source] = stream + else: + # Update keepalive option on existing stream + stream.keepalive = keepalive # Add provider stream.add_provider(fmt) diff --git a/tests/components/camera/common.py b/tests/components/camera/common.py index 21f7244bd299c9..bebb991a7af90a 100644 --- a/tests/components/camera/common.py +++ b/tests/components/camera/common.py @@ -4,7 +4,9 @@ components. Instead call the service directly. """ from homeassistant.components.camera import ( - ATTR_FILENAME, DOMAIN, SERVICE_ENABLE_MOTION, SERVICE_SNAPSHOT) + ATTR_FILENAME, SERVICE_ENABLE_MOTION, SERVICE_SNAPSHOT) +from homeassistant.components.camera.const import ( + DOMAIN, DATA_CAMERA_PREFS, PREF_PRELOAD_STREAM) from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, \ SERVICE_TURN_ON from homeassistant.core import callback @@ -45,3 +47,13 @@ def async_snapshot(hass, filename, entity_id=None): hass.async_add_job(hass.services.async_call( DOMAIN, SERVICE_SNAPSHOT, data)) + + +def mock_camera_prefs(hass, entity_id, prefs={}): + """Fixture for cloud component.""" + prefs_to_set = { + PREF_PRELOAD_STREAM: True, + } + prefs_to_set.update(prefs) + hass.data[DATA_CAMERA_PREFS]._prefs[entity_id] = prefs_to_set + return prefs_to_set diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 0359d14df63596..701a368283083a 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -1,13 +1,17 @@ """The tests for the camera component.""" import asyncio import base64 +import io from unittest.mock import patch, mock_open, PropertyMock import pytest from homeassistant.setup import setup_component, async_setup_component -from homeassistant.const import (ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE, EVENT_HOMEASSISTANT_START) from homeassistant.components import camera, http +from homeassistant.components.camera.const import DOMAIN, PREF_PRELOAD_STREAM +from homeassistant.components.camera.prefs import CameraEntityPreferences from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.exceptions import HomeAssistantError from homeassistant.util.async_ import run_coroutine_threadsafe @@ -16,7 +20,6 @@ get_test_home_assistant, get_test_instance_port, assert_setup_component, mock_coro) from tests.components.camera import common -from tests.components.stream.common import generate_h264_video @pytest.fixture @@ -41,6 +44,12 @@ def mock_stream(hass): })) +@pytest.fixture +def setup_camera_prefs(hass): + """Initialize HTTP API.""" + return common.mock_camera_prefs(hass, 'camera.demo_camera') + + class TestSetupCamera: """Test class for setup camera.""" @@ -146,7 +155,7 @@ def test_snapshot_service(hass, mock_camera): assert mock_write.mock_calls[0][1][0] == b'Test' -async def test_webocket_camera_thumbnail(hass, hass_ws_client, mock_camera): +async def test_websocket_camera_thumbnail(hass, hass_ws_client, mock_camera): """Test camera_thumbnail websocket command.""" await async_setup_component(hass, 'camera') @@ -167,8 +176,8 @@ async def test_webocket_camera_thumbnail(hass, hass_ws_client, mock_camera): base64.b64encode(b'Test').decode('utf-8') -async def test_webocket_stream_no_source(hass, hass_ws_client, - mock_camera, mock_stream): +async def test_websocket_stream_no_source(hass, hass_ws_client, + mock_camera, mock_stream): """Test camera/stream websocket command.""" await async_setup_component(hass, 'camera') @@ -191,8 +200,8 @@ async def test_webocket_stream_no_source(hass, hass_ws_client, assert not msg['success'] -async def test_webocket_camera_stream(hass, hass_ws_client, hass_client, - mock_camera, mock_stream): +async def test_websocket_camera_stream(hass, hass_ws_client, + mock_camera, mock_stream): """Test camera/stream websocket command.""" await async_setup_component(hass, 'camera') @@ -201,7 +210,7 @@ async def test_webocket_camera_stream(hass, hass_ws_client, hass_client, ) as mock_request_stream, \ patch('homeassistant.components.demo.camera.DemoCamera.stream_source', new_callable=PropertyMock) as mock_stream_source: - mock_stream_source.return_value = generate_h264_video() + mock_stream_source.return_value = io.BytesIO() # Request playlist through WebSocket client = await hass_ws_client(hass) await client.send_json({ @@ -219,6 +228,44 @@ async def test_webocket_camera_stream(hass, hass_ws_client, hass_client, assert msg['result']['url'][-13:] == 'playlist.m3u8' +async def test_websocket_get_prefs(hass, hass_ws_client, + mock_camera): + """Test get camera preferences websocket command.""" + await async_setup_component(hass, 'camera') + + # Request preferences through websocket + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 7, + 'type': 'camera/get_prefs', + 'entity_id': 'camera.demo_camera', + }) + msg = await client.receive_json() + + # Assert WebSocket response + assert msg['success'] + + +async def test_websocket_update_prefs(hass, hass_ws_client, + mock_camera, setup_camera_prefs): + """Test updating preference.""" + await async_setup_component(hass, 'camera') + assert setup_camera_prefs[PREF_PRELOAD_STREAM] + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 8, + 'type': 'camera/update_prefs', + 'entity_id': 'camera.demo_camera', + 'preload_stream': False, + }) + response = await client.receive_json() + + assert response['success'] + assert not setup_camera_prefs[PREF_PRELOAD_STREAM] + assert response['result'][PREF_PRELOAD_STREAM] == \ + setup_camera_prefs[PREF_PRELOAD_STREAM] + + async def test_play_stream_service_no_source(hass, mock_camera, mock_stream): """Test camera play_stream service.""" data = { @@ -243,10 +290,54 @@ async def test_handle_play_stream_service(hass, mock_camera, mock_stream): ) as mock_request_stream, \ patch('homeassistant.components.demo.camera.DemoCamera.stream_source', new_callable=PropertyMock) as mock_stream_source: - mock_stream_source.return_value = generate_h264_video() + mock_stream_source.return_value = io.BytesIO() # Call service await hass.services.async_call( camera.DOMAIN, camera.SERVICE_PLAY_STREAM, data, blocking=True) # So long as we request the stream, the rest should be covered # by the play_media service tests. assert mock_request_stream.called + + +async def test_no_preload_stream(hass, mock_stream): + """Test camera preload preference.""" + demo_prefs = CameraEntityPreferences({ + PREF_PRELOAD_STREAM: False, + }) + with patch('homeassistant.components.camera.request_stream' + ) as mock_request_stream, \ + patch('homeassistant.components.camera.prefs.CameraPreferences.get', + return_value=demo_prefs), \ + patch('homeassistant.components.demo.camera.DemoCamera.stream_source', + new_callable=PropertyMock) as mock_stream_source: + mock_stream_source.return_value = io.BytesIO() + await async_setup_component(hass, 'camera', { + DOMAIN: { + 'platform': 'demo' + } + }) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + assert not mock_request_stream.called + + +async def test_preload_stream(hass, mock_stream): + """Test camera preload preference.""" + demo_prefs = CameraEntityPreferences({ + PREF_PRELOAD_STREAM: True, + }) + with patch('homeassistant.components.camera.request_stream' + ) as mock_request_stream, \ + patch('homeassistant.components.camera.prefs.CameraPreferences.get', + return_value=demo_prefs), \ + patch('homeassistant.components.demo.camera.DemoCamera.stream_source', + new_callable=PropertyMock) as mock_stream_source: + mock_stream_source.return_value = io.BytesIO() + await async_setup_component(hass, 'camera', { + DOMAIN: { + 'platform': 'demo' + } + }) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + assert mock_request_stream.called diff --git a/tests/components/local_file/test_camera.py b/tests/components/local_file/test_camera.py index 3d70e3f77a7916..a96f9768be4b67 100644 --- a/tests/components/local_file/test_camera.py +++ b/tests/components/local_file/test_camera.py @@ -2,7 +2,7 @@ import asyncio from unittest import mock -from homeassistant.components.camera import DOMAIN +from homeassistant.components.camera.const import DOMAIN from homeassistant.components.local_file.camera import ( SERVICE_UPDATE_FILE_PATH) from homeassistant.setup import async_setup_component From 65432ba5523ec8385bfd40a968a72726ad6b150f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 26 Mar 2019 05:38:33 -0700 Subject: [PATCH 159/605] Move core stuff into Home Assistant integration (#22407) * Move core stuff into Home Assistant integration * Lint --- homeassistant/bootstrap.py | 10 +- homeassistant/components/__init__.py | 135 +---------------- homeassistant/components/config/customize.py | 2 +- homeassistant/components/hassio/__init__.py | 2 +- .../components/homeassistant/__init__.py | 137 ++++++++++++++++++ .../scene.py} | 2 +- tests/components/conversation/test_init.py | 13 +- tests/components/cover/test_init.py | 2 +- tests/components/emulated_hue/test_hue_api.py | 7 +- .../generic_thermostat/test_climate.py | 3 +- .../google_assistant/test_google_assistant.py | 4 +- tests/components/homeassistant/__init__.py | 1 + .../{init => homeassistant}/test_init.py | 20 +-- tests/components/init/__init__.py | 1 - tests/helpers/test_state.py | 6 +- 15 files changed, 175 insertions(+), 170 deletions(-) create mode 100644 homeassistant/components/homeassistant/__init__.py rename homeassistant/components/{scene/homeassistant.py => homeassistant/scene.py} (98%) create mode 100644 tests/components/homeassistant/__init__.py rename tests/components/{init => homeassistant}/test_init.py (94%) delete mode 100644 tests/components/init/__init__.py diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index d532d9cdb8685b..a3b1d6d305e368 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -9,10 +9,10 @@ import voluptuous as vol -from homeassistant import ( - core, config as conf_util, config_entries, components as core_components, - loader) -from homeassistant.components import persistent_notification +from homeassistant import core, config as conf_util, config_entries, loader +from homeassistant.components import ( + persistent_notification, homeassistant as core_component +) from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE from homeassistant.setup import async_setup_component from homeassistant.util.logging import AsyncHandler @@ -139,7 +139,7 @@ async def async_from_config_dict(config: Dict[str, Any], pass # setup components - res = await core_components.async_setup(hass, config) + res = await core_component.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " "Further initialization aborted") diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index 533811e275d5cd..88cd44f4bf2762 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -7,33 +7,12 @@ format ".". - Each component should publish services only under its own domain. """ -import asyncio -import itertools as it import logging -from typing import Awaitable -import voluptuous as vol - -import homeassistant.core as ha -import homeassistant.config as conf_util -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.service import async_extract_entity_ids -from homeassistant.helpers import intent -from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, - SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART, - RESTART_EXIT_CODE) -from homeassistant.helpers import config_validation as cv +from homeassistant.core import split_entity_id _LOGGER = logging.getLogger(__name__) -SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config' -SERVICE_CHECK_CONFIG = 'check_config' -SERVICE_UPDATE_ENTITY = 'update_entity' -SCHEMA_UPDATE_ENTITY = vol.Schema({ - ATTR_ENTITY_ID: cv.entity_ids -}) - def is_on(hass, entity_id=None): """Load up the module to call the is_on method. @@ -46,7 +25,7 @@ def is_on(hass, entity_id=None): entity_ids = hass.states.entity_ids() for ent_id in entity_ids: - domain = ha.split_entity_id(ent_id)[0] + domain = split_entity_id(ent_id)[0] try: component = getattr(hass.components, domain) @@ -64,113 +43,3 @@ def is_on(hass, entity_id=None): return True return False - - -async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]: - """Set up general services related to Home Assistant.""" - async def async_handle_turn_service(service): - """Handle calls to homeassistant.turn_on/off.""" - entity_ids = await async_extract_entity_ids(hass, service) - - # Generic turn on/off method requires entity id - if not entity_ids: - _LOGGER.error( - "homeassistant/%s cannot be called without entity_id", - service.service) - return - - # Group entity_ids by domain. groupby requires sorted data. - by_domain = it.groupby(sorted(entity_ids), - lambda item: ha.split_entity_id(item)[0]) - - tasks = [] - - for domain, ent_ids in by_domain: - # We want to block for all calls and only return when all calls - # have been processed. If a service does not exist it causes a 10 - # second delay while we're blocking waiting for a response. - # But services can be registered on other HA instances that are - # listening to the bus too. So as an in between solution, we'll - # block only if the service is defined in the current HA instance. - blocking = hass.services.has_service(domain, service.service) - - # Create a new dict for this call - data = dict(service.data) - - # ent_ids is a generator, convert it to a list. - data[ATTR_ENTITY_ID] = list(ent_ids) - - tasks.append(hass.services.async_call( - domain, service.service, data, blocking)) - - await asyncio.wait(tasks, loop=hass.loop) - - hass.services.async_register( - ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service) - hass.services.async_register( - ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service) - hass.services.async_register( - ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service) - hass.helpers.intent.async_register(intent.ServiceIntentHandler( - intent.INTENT_TURN_ON, ha.DOMAIN, SERVICE_TURN_ON, "Turned {} on")) - hass.helpers.intent.async_register(intent.ServiceIntentHandler( - intent.INTENT_TURN_OFF, ha.DOMAIN, SERVICE_TURN_OFF, - "Turned {} off")) - hass.helpers.intent.async_register(intent.ServiceIntentHandler( - intent.INTENT_TOGGLE, ha.DOMAIN, SERVICE_TOGGLE, "Toggled {}")) - - async def async_handle_core_service(call): - """Service handler for handling core services.""" - if call.service == SERVICE_HOMEASSISTANT_STOP: - hass.async_create_task(hass.async_stop()) - return - - try: - errors = await conf_util.async_check_ha_config_file(hass) - except HomeAssistantError: - return - - if errors: - _LOGGER.error(errors) - hass.components.persistent_notification.async_create( - "Config error. See dev-info panel for details.", - "Config validating", "{0}.check_config".format(ha.DOMAIN)) - return - - if call.service == SERVICE_HOMEASSISTANT_RESTART: - hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE)) - - async def async_handle_update_service(call): - """Service handler for updating an entity.""" - tasks = [hass.helpers.entity_component.async_update_entity(entity) - for entity in call.data[ATTR_ENTITY_ID]] - - if tasks: - await asyncio.wait(tasks) - - hass.services.async_register( - ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service) - hass.services.async_register( - ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service) - hass.services.async_register( - ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service) - hass.services.async_register( - ha.DOMAIN, SERVICE_UPDATE_ENTITY, async_handle_update_service, - schema=SCHEMA_UPDATE_ENTITY) - - async def async_handle_reload_config(call): - """Service handler for reloading core config.""" - try: - conf = await conf_util.async_hass_config_yaml(hass) - except HomeAssistantError as err: - _LOGGER.error(err) - return - - # auth only processed during startup - await conf_util.async_process_ha_core_config( - hass, conf.get(ha.DOMAIN) or {}) - - hass.services.async_register( - ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config) - - return True diff --git a/homeassistant/components/config/customize.py b/homeassistant/components/config/customize.py index bb774ae7ef8e0d..85e9c0e6886706 100644 --- a/homeassistant/components/config/customize.py +++ b/homeassistant/components/config/customize.py @@ -1,5 +1,5 @@ """Provide configuration end points for Customize.""" -from homeassistant.components import SERVICE_RELOAD_CORE_CONFIG +from homeassistant.components.homeassistant import SERVICE_RELOAD_CORE_CONFIG from homeassistant.config import DATA_CUSTOMIZE from homeassistant.core import DOMAIN import homeassistant.helpers.config_validation as cv diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 7e47ac152e32c9..90e120d8b0eafe 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -6,7 +6,7 @@ import voluptuous as vol from homeassistant.auth.const import GROUP_ID_ADMIN -from homeassistant.components import SERVICE_CHECK_CONFIG +from homeassistant.components.homeassistant import SERVICE_CHECK_CONFIG import homeassistant.config as conf_util from homeassistant.const import ( ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP) diff --git a/homeassistant/components/homeassistant/__init__.py b/homeassistant/components/homeassistant/__init__.py new file mode 100644 index 00000000000000..ef01d133cff623 --- /dev/null +++ b/homeassistant/components/homeassistant/__init__.py @@ -0,0 +1,137 @@ +"""Integration providing core pieces of infrastructure.""" +import asyncio +import itertools as it +import logging +from typing import Awaitable + +import voluptuous as vol + +import homeassistant.core as ha +import homeassistant.config as conf_util +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.service import async_extract_entity_ids +from homeassistant.helpers import intent +from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, + SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART, + RESTART_EXIT_CODE) +from homeassistant.helpers import config_validation as cv + +_LOGGER = logging.getLogger(__name__) +DOMAIN = ha.DOMAIN +SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config' +SERVICE_CHECK_CONFIG = 'check_config' +SERVICE_UPDATE_ENTITY = 'update_entity' +SCHEMA_UPDATE_ENTITY = vol.Schema({ + ATTR_ENTITY_ID: cv.entity_ids +}) + + +async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]: + """Set up general services related to Home Assistant.""" + async def async_handle_turn_service(service): + """Handle calls to homeassistant.turn_on/off.""" + entity_ids = await async_extract_entity_ids(hass, service) + + # Generic turn on/off method requires entity id + if not entity_ids: + _LOGGER.error( + "homeassistant/%s cannot be called without entity_id", + service.service) + return + + # Group entity_ids by domain. groupby requires sorted data. + by_domain = it.groupby(sorted(entity_ids), + lambda item: ha.split_entity_id(item)[0]) + + tasks = [] + + for domain, ent_ids in by_domain: + # We want to block for all calls and only return when all calls + # have been processed. If a service does not exist it causes a 10 + # second delay while we're blocking waiting for a response. + # But services can be registered on other HA instances that are + # listening to the bus too. So as an in between solution, we'll + # block only if the service is defined in the current HA instance. + blocking = hass.services.has_service(domain, service.service) + + # Create a new dict for this call + data = dict(service.data) + + # ent_ids is a generator, convert it to a list. + data[ATTR_ENTITY_ID] = list(ent_ids) + + tasks.append(hass.services.async_call( + domain, service.service, data, blocking)) + + await asyncio.wait(tasks, loop=hass.loop) + + hass.services.async_register( + ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service) + hass.helpers.intent.async_register(intent.ServiceIntentHandler( + intent.INTENT_TURN_ON, ha.DOMAIN, SERVICE_TURN_ON, "Turned {} on")) + hass.helpers.intent.async_register(intent.ServiceIntentHandler( + intent.INTENT_TURN_OFF, ha.DOMAIN, SERVICE_TURN_OFF, + "Turned {} off")) + hass.helpers.intent.async_register(intent.ServiceIntentHandler( + intent.INTENT_TOGGLE, ha.DOMAIN, SERVICE_TOGGLE, "Toggled {}")) + + async def async_handle_core_service(call): + """Service handler for handling core services.""" + if call.service == SERVICE_HOMEASSISTANT_STOP: + hass.async_create_task(hass.async_stop()) + return + + try: + errors = await conf_util.async_check_ha_config_file(hass) + except HomeAssistantError: + return + + if errors: + _LOGGER.error(errors) + hass.components.persistent_notification.async_create( + "Config error. See dev-info panel for details.", + "Config validating", "{0}.check_config".format(ha.DOMAIN)) + return + + if call.service == SERVICE_HOMEASSISTANT_RESTART: + hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE)) + + async def async_handle_update_service(call): + """Service handler for updating an entity.""" + tasks = [hass.helpers.entity_component.async_update_entity(entity) + for entity in call.data[ATTR_ENTITY_ID]] + + if tasks: + await asyncio.wait(tasks) + + hass.services.async_register( + ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service) + hass.services.async_register( + ha.DOMAIN, SERVICE_UPDATE_ENTITY, async_handle_update_service, + schema=SCHEMA_UPDATE_ENTITY) + + async def async_handle_reload_config(call): + """Service handler for reloading core config.""" + try: + conf = await conf_util.async_hass_config_yaml(hass) + except HomeAssistantError as err: + _LOGGER.error(err) + return + + # auth only processed during startup + await conf_util.async_process_ha_core_config( + hass, conf.get(ha.DOMAIN) or {}) + + hass.services.async_register( + ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config) + + return True diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/homeassistant/scene.py similarity index 98% rename from homeassistant/components/scene/homeassistant.py rename to homeassistant/components/homeassistant/scene.py index 86af6c34694a00..617b56241108da 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/homeassistant/scene.py @@ -9,8 +9,8 @@ from homeassistant.core import State import homeassistant.helpers.config_validation as cv from homeassistant.helpers.state import HASS_DOMAIN, async_reproduce_state +from homeassistant.components.scene import STATES, Scene -from . import STATES, Scene PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): HASS_DOMAIN, diff --git a/tests/components/conversation/test_init.py b/tests/components/conversation/test_init.py index 2aa1f499a768ed..812456a3594a2d 100644 --- a/tests/components/conversation/test_init.py +++ b/tests/components/conversation/test_init.py @@ -5,7 +5,6 @@ from homeassistant.core import DOMAIN as HASS_DOMAIN from homeassistant.setup import async_setup_component from homeassistant.components import conversation -import homeassistant.components as component from homeassistant.components.cover import (SERVICE_OPEN_COVER) from homeassistant.helpers import intent @@ -16,7 +15,7 @@ async def test_calling_intent(hass): """Test calling an intent from a conversation.""" intents = async_mock_intent(hass, 'OrderBeer') - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', { @@ -146,7 +145,7 @@ async def async_handle(self, intent): @pytest.mark.parametrize('sentence', ('turn on kitchen', 'turn kitchen on')) async def test_turn_on_intent(hass, sentence): """Test calling the turn on intent.""" - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', {}) @@ -197,7 +196,7 @@ async def test_cover_intents_loading(hass): @pytest.mark.parametrize('sentence', ('turn off kitchen', 'turn kitchen off')) async def test_turn_off_intent(hass, sentence): """Test calling the turn on intent.""" - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', {}) @@ -222,7 +221,7 @@ async def test_turn_off_intent(hass, sentence): @pytest.mark.parametrize('sentence', ('toggle kitchen', 'kitchen toggle')) async def test_toggle_intent(hass, sentence): """Test calling the turn on intent.""" - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', {}) @@ -246,7 +245,7 @@ async def test_toggle_intent(hass, sentence): async def test_http_api(hass, hass_client): """Test the HTTP conversation API.""" - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', {}) @@ -270,7 +269,7 @@ async def test_http_api(hass, hass_client): async def test_http_api_wrong_data(hass, hass_client): """Test the HTTP conversation API.""" - result = await component.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result result = await async_setup_component(hass, 'conversation', {}) diff --git a/tests/components/cover/test_init.py b/tests/components/cover/test_init.py index 5df492d3d47149..09cb5752b552bc 100644 --- a/tests/components/cover/test_init.py +++ b/tests/components/cover/test_init.py @@ -2,7 +2,7 @@ from homeassistant.components.cover import (SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER) -from homeassistant.components import intent +from homeassistant.helpers import intent import homeassistant.components as comps from tests.common import async_mock_service diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 8be99a02148662..08001b0ebab522 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -8,8 +8,7 @@ import pytest from tests.common import get_test_instance_port -from homeassistant import core, const, setup -import homeassistant.components as core_components +from homeassistant import const, setup from homeassistant.components import ( fan, http, light, script, emulated_hue, media_player, cover, climate) from homeassistant.components.emulated_hue import Config @@ -33,8 +32,8 @@ def hass_hue(loop, hass): """Set up a Home Assistant instance for these tests.""" # We need to do this to get access to homeassistant/turn_(on,off) - loop.run_until_complete( - core_components.async_setup(hass, {core.DOMAIN: {}})) + loop.run_until_complete(setup.async_setup_component( + hass, 'homeassistant', {})) loop.run_until_complete(setup.async_setup_component( hass, http.DOMAIN, diff --git a/tests/components/generic_thermostat/test_climate.py b/tests/components/generic_thermostat/test_climate.py index 1d532f4757cff3..49d49fdd3d404b 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -23,7 +23,6 @@ from homeassistant.components import input_boolean, switch from homeassistant.components.climate.const import ( ATTR_OPERATION_MODE, STATE_HEAT, STATE_COOL, DOMAIN) -import homeassistant.components as comps from tests.common import assert_setup_component, mock_restore_cache from tests.components.climate import common @@ -68,7 +67,7 @@ def setup_comp_1(hass): """Initialize components.""" hass.config.units = METRIC_SYSTEM assert hass.loop.run_until_complete( - comps.async_setup(hass, {}) + async_setup_component(hass, 'homeassistant', {}) ) diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 69964a11fdcbf1..60df4a8e79c8ff 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -8,7 +8,7 @@ from homeassistant import core, const, setup from homeassistant.components import ( - fan, cover, light, switch, lock, async_setup, media_player) + fan, cover, light, switch, lock, media_player) from homeassistant.components.climate import const as climate from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES from homeassistant.components import google_assistant as ga @@ -56,7 +56,7 @@ def assistant_client(loop, hass, aiohttp_client): def hass_fixture(loop, hass): """Set up a Home Assistant instance for these tests.""" # We need to do this to get access to homeassistant/turn_(on,off) - loop.run_until_complete(async_setup(hass, {core.DOMAIN: {}})) + loop.run_until_complete(setup.async_setup_component(hass, core.DOMAIN, {})) loop.run_until_complete( setup.async_setup_component(hass, light.DOMAIN, { diff --git a/tests/components/homeassistant/__init__.py b/tests/components/homeassistant/__init__.py new file mode 100644 index 00000000000000..334751e6de5613 --- /dev/null +++ b/tests/components/homeassistant/__init__.py @@ -0,0 +1 @@ +"""Tests for the Home Assistant integration to provide core functionality.""" diff --git a/tests/components/init/test_init.py b/tests/components/homeassistant/test_init.py similarity index 94% rename from tests/components/init/test_init.py rename to tests/components/homeassistant/test_init.py index da06927db3a0c1..b72589e60e3238 100644 --- a/tests/components/init/test_init.py +++ b/tests/components/homeassistant/test_init.py @@ -12,7 +12,8 @@ SERVICE_HOMEASSISTANT_STOP, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE) import homeassistant.components as comps -from homeassistant.components import ( +from homeassistant.setup import async_setup_component +from homeassistant.components.homeassistant import ( SERVICE_CHECK_CONFIG, SERVICE_RELOAD_CORE_CONFIG) import homeassistant.helpers.intent as intent from homeassistant.exceptions import HomeAssistantError @@ -97,7 +98,8 @@ def setUp(self): """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() assert run_coroutine_threadsafe( - comps.async_setup(self.hass, {}), self.hass.loop + async_setup_component(self.hass, 'homeassistant', {}), + self.hass.loop ).result() self.hass.states.set('light.Bowl', STATE_ON) @@ -186,7 +188,7 @@ def test_reload_core_conf(self): assert state.attributes.get('hello') == 'world' @patch('homeassistant.config.os.path.isfile', Mock(return_value=True)) - @patch('homeassistant.components._LOGGER.error') + @patch('homeassistant.components.homeassistant._LOGGER.error') @patch('homeassistant.config.async_process_ha_core_config') def test_reload_core_with_wrong_conf(self, mock_process, mock_error): """Test reload core conf service.""" @@ -244,7 +246,7 @@ def test_check_config(self, mock_check, mock_stop): async def test_turn_on_intent(hass): """Test HassTurnOn intent.""" - result = await comps.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result hass.states.async_set('light.test_light', 'off') @@ -265,7 +267,7 @@ async def test_turn_on_intent(hass): async def test_turn_off_intent(hass): """Test HassTurnOff intent.""" - result = await comps.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result hass.states.async_set('light.test_light', 'on') @@ -286,7 +288,7 @@ async def test_turn_off_intent(hass): async def test_toggle_intent(hass): """Test HassToggle intent.""" - result = await comps.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result hass.states.async_set('light.test_light', 'off') @@ -310,7 +312,7 @@ async def test_turn_on_multiple_intent(hass): This tests that matching finds the proper entity among similar names. """ - result = await comps.async_setup(hass, {}) + result = await async_setup_component(hass, 'homeassistant', {}) assert result hass.states.async_set('light.test_light', 'off') @@ -333,7 +335,7 @@ async def test_turn_on_multiple_intent(hass): async def test_turn_on_to_not_block_for_domains_without_service(hass): """Test if turn_on is blocking domain with no service.""" - await comps.async_setup(hass, {}) + await async_setup_component(hass, 'homeassistant', {}) async_mock_service(hass, 'light', SERVICE_TURN_ON) hass.states.async_set('light.Bowl', STATE_ON) hass.states.async_set('light.Ceiling', STATE_OFF) @@ -359,7 +361,7 @@ async def test_turn_on_to_not_block_for_domains_without_service(hass): async def test_entity_update(hass): """Test being able to call entity update.""" - await comps.async_setup(hass, {}) + await async_setup_component(hass, 'homeassistant', {}) with patch('homeassistant.helpers.entity_component.async_update_entity', return_value=mock_coro()) as mock_update: diff --git a/tests/components/init/__init__.py b/tests/components/init/__init__.py deleted file mode 100644 index b935cf060c8170..00000000000000 --- a/tests/components/init/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the init component.""" diff --git a/tests/helpers/test_state.py b/tests/helpers/test_state.py index 5c04f085c86226..10b053528ab6fd 100644 --- a/tests/helpers/test_state.py +++ b/tests/helpers/test_state.py @@ -5,7 +5,7 @@ from unittest.mock import patch import homeassistant.core as ha -import homeassistant.components as core_components +from homeassistant.setup import async_setup_component from homeassistant.const import (SERVICE_TURN_ON, SERVICE_TURN_OFF) from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.util import dt as dt_util @@ -88,8 +88,8 @@ class TestStateHelpers(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Run when tests are started.""" self.hass = get_test_home_assistant() - run_coroutine_threadsafe(core_components.async_setup( - self.hass, {}), self.hass.loop).result() + run_coroutine_threadsafe(async_setup_component( + self.hass, 'homeassistant', {}), self.hass.loop).result() def tearDown(self): # pylint: disable=invalid-name """Stop when tests are finished.""" From 77e7b63f4a2e5ef272c41ae72ef007c4892c7b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 26 Mar 2019 14:02:10 +0100 Subject: [PATCH 160/605] Tibber add support for Watty (#22397) --- homeassistant/components/tibber/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index eeae1587099b01..135437801d934b 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.9.9'] +REQUIREMENTS = ['pyTibber==0.10.0'] DOMAIN = 'tibber' diff --git a/requirements_all.txt b/requirements_all.txt index 401d22cd551371..8ad56528f4f0ae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -925,7 +925,7 @@ pyRFXtrx==0.23 # pySwitchmate==0.4.5 # homeassistant.components.tibber -pyTibber==0.9.9 +pyTibber==0.10.0 # homeassistant.components.dlink.switch pyW215==0.6.0 From 3cca3c37f04098fa76c7dd4edba6d81d5bd65a57 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Tue, 26 Mar 2019 09:17:43 -0400 Subject: [PATCH 161/605] zha fixes (#22381) --- homeassistant/components/zha/api.py | 21 ++++++++----------- .../components/zha/core/channels/general.py | 11 +++++++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 4b4546821a7c75..8bfcbc705dc7cb 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -310,11 +310,10 @@ async def websocket_read_zigbee_cluster_attributes(hass, connection, msg): cluster_id = msg[ATTR_CLUSTER_ID] cluster_type = msg[ATTR_CLUSTER_TYPE] attribute = msg[ATTR_ATTRIBUTE] - manufacturer = None - # only use manufacturer code for manufacturer clusters - if cluster_id >= MFG_CLUSTER_ID_START: - manufacturer = msg.get(ATTR_MANUFACTURER) or None + manufacturer = msg.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) + if cluster_id >= MFG_CLUSTER_ID_START and manufacturer is None: + manufacturer = zha_device.manufacturer_code success = failure = None if zha_device is not None: cluster = zha_device.async_get_cluster( @@ -476,11 +475,10 @@ async def set_zigbee_cluster_attributes(service): cluster_type = service.data.get(ATTR_CLUSTER_TYPE) attribute = service.data.get(ATTR_ATTRIBUTE) value = service.data.get(ATTR_VALUE) - manufacturer = None - # only use manufacturer code for manufacturer clusters - if cluster_id >= MFG_CLUSTER_ID_START: - manufacturer = service.data.get(ATTR_MANUFACTURER) or None + manufacturer = service.data.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) + if cluster_id >= MFG_CLUSTER_ID_START and manufacturer is None: + manufacturer = zha_device.manufacturer_code response = None if zha_device is not None: response = await zha_device.write_zigbee_attribute( @@ -517,11 +515,10 @@ async def issue_zigbee_cluster_command(service): command = service.data.get(ATTR_COMMAND) command_type = service.data.get(ATTR_COMMAND_TYPE) args = service.data.get(ATTR_ARGS) - manufacturer = None - # only use manufacturer code for manufacturer clusters - if cluster_id >= MFG_CLUSTER_ID_START: - manufacturer = service.data.get(ATTR_MANUFACTURER) or None + manufacturer = service.data.get(ATTR_MANUFACTURER) or None zha_device = zha_gateway.get_device(ieee) + if cluster_id >= MFG_CLUSTER_ID_START and manufacturer is None: + manufacturer = zha_device.manufacturer_code response = None if zha_device is not None: response = await zha_device.issue_cluster_command( diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index bf0f1044efb67c..061541d4dae4e5 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -7,7 +7,7 @@ import logging from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send -from . import ZigbeeChannel, parse_and_log_command +from . import ZigbeeChannel, parse_and_log_command, MAINS_POWERED from ..helpers import get_attr_id_by_name from ..const import ( SIGNAL_ATTR_UPDATED, SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, @@ -64,9 +64,14 @@ async def async_initialize(self, from_cache): async def async_update(self): """Initialize channel.""" - _LOGGER.debug("Attempting to update onoff state") + from_cache = not self.device.power_source == MAINS_POWERED + _LOGGER.debug( + "%s is attempting to update onoff state - from cache: %s", + self._unique_id, + from_cache + ) self._state = bool( - await self.get_attribute_value(self.ON_OFF, from_cache=False)) + await self.get_attribute_value(self.ON_OFF, from_cache=from_cache)) await super().async_update() From 2cebf9ef71c88e3db5ddcfab7a78cbba20f83e15 Mon Sep 17 00:00:00 2001 From: zewelor Date: Tue, 26 Mar 2019 14:18:53 +0100 Subject: [PATCH 162/605] Fix yeelight state update (#22373) --- homeassistant/components/yeelight/__init__.py | 4 ---- homeassistant/components/yeelight/light.py | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 8a5c1e81a93fcc..7318b088ab4da3 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -259,8 +259,6 @@ def turn_on(self, duration=DEFAULT_TRANSITION): _LOGGER.error("Unable to turn the bulb on: %s", ex) return - self.update() - def turn_off(self, duration=DEFAULT_TRANSITION): """Turn off device.""" import yeelight @@ -271,8 +269,6 @@ def turn_off(self, duration=DEFAULT_TRANSITION): _LOGGER.error("Unable to turn the bulb on: %s", ex) return - self.update() - def update(self): """Read new properties from the device.""" if not self.bulb: diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index d208d1f72b0a08..22e5d9cc9cefed 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -494,6 +494,7 @@ def turn_on(self, **kwargs) -> None: except yeelight.BulbException as ex: _LOGGER.error("Unable to set the defaults: %s", ex) return + self.device.update() def turn_off(self, **kwargs) -> None: """Turn off.""" @@ -502,6 +503,7 @@ def turn_off(self, **kwargs) -> None: duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s self.device.turn_off(duration=duration) + self.device.update() def set_mode(self, mode: str): """Set a power mode.""" @@ -509,11 +511,10 @@ def set_mode(self, mode: str): try: self._bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) + self.device.update() except yeelight.BulbException as ex: _LOGGER.error("Unable to set the power mode: %s", ex) - self.device.update() - def start_flow(self, transitions, count=0, action=ACTION_RECOVER): """Start flow.""" import yeelight From c71e5ed588db54e4a6fbe0ddaed69b8f7b540d31 Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Tue, 26 Mar 2019 09:20:50 -0400 Subject: [PATCH 163/605] Changed busy error to warning (#22398) --- homeassistant/components/radiotherm/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/radiotherm/climate.py b/homeassistant/components/radiotherm/climate.py index bad20884536f41..4132d3c27c743f 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -246,8 +246,8 @@ def update(self): try: data = self.device.tstat['raw'] except radiotherm.validate.RadiothermTstatError: - _LOGGER.error('%s (%s) was busy (invalid value returned)', - self._name, self.device.host) + _LOGGER.warning('%s (%s) was busy (invalid value returned)', + self._name, self.device.host) return current_temp = data['temp'] From a27e821e8b73499d640604e3039a649c37c48b5a Mon Sep 17 00:00:00 2001 From: cgtobi Date: Tue, 26 Mar 2019 15:34:16 +0100 Subject: [PATCH 164/605] Migrate tts (#22403) * Migrate tts * Migrate tts tests * Update requirements * Fix path to demo mp3 --- .../{tts/amazon_polly.py => amazon_polly/tts.py} | 0 .../components/{tts/baidu.py => baidu/tts.py} | 0 homeassistant/components/demo/mailbox.py | 2 +- .../components/{tts/demo.mp3 => demo/tts.mp3} | Bin .../components/{tts/demo.py => demo/tts.py} | 4 ++-- .../components/{tts/marytts.py => marytts/tts.py} | 2 +- .../{tts/microsoft.py => microsoft/tts.py} | 0 .../components/{tts/picotts.py => picotts/tts.py} | 0 .../components/{tts/voicerss.py => voicerss/tts.py} | 2 +- .../{tts/yandextts.py => yandextts/tts.py} | 2 +- requirements_all.txt | 7 ------- tests/components/marytts/__init__.py | 1 + .../{tts/test_marytts.py => marytts/test_tts.py} | 2 +- tests/components/tts/test_init.py | 8 ++++---- tests/components/voicerss/__init__.py | 1 + .../{tts/test_voicerss.py => voicerss/test_tts.py} | 2 +- tests/components/yandextts/__init__.py | 1 + .../test_yandextts.py => yandextts/test_tts.py} | 2 +- 18 files changed, 16 insertions(+), 20 deletions(-) rename homeassistant/components/{tts/amazon_polly.py => amazon_polly/tts.py} (100%) rename homeassistant/components/{tts/baidu.py => baidu/tts.py} (100%) rename homeassistant/components/{tts/demo.mp3 => demo/tts.mp3} (100%) rename homeassistant/components/{tts/demo.py => demo/tts.py} (90%) rename homeassistant/components/{tts/marytts.py => marytts/tts.py} (97%) rename homeassistant/components/{tts/microsoft.py => microsoft/tts.py} (100%) rename homeassistant/components/{tts/picotts.py => picotts/tts.py} (100%) rename homeassistant/components/{tts/voicerss.py => voicerss/tts.py} (98%) rename homeassistant/components/{tts/yandextts.py => yandextts/tts.py} (98%) create mode 100644 tests/components/marytts/__init__.py rename tests/components/{tts/test_marytts.py => marytts/test_tts.py} (98%) create mode 100644 tests/components/voicerss/__init__.py rename tests/components/{tts/test_voicerss.py => voicerss/test_tts.py} (99%) create mode 100644 tests/components/yandextts/__init__.py rename tests/components/{tts/test_yandextts.py => yandextts/test_tts.py} (99%) diff --git a/homeassistant/components/tts/amazon_polly.py b/homeassistant/components/amazon_polly/tts.py similarity index 100% rename from homeassistant/components/tts/amazon_polly.py rename to homeassistant/components/amazon_polly/tts.py diff --git a/homeassistant/components/tts/baidu.py b/homeassistant/components/baidu/tts.py similarity index 100% rename from homeassistant/components/tts/baidu.py rename to homeassistant/components/baidu/tts.py diff --git a/homeassistant/components/demo/mailbox.py b/homeassistant/components/demo/mailbox.py index 885988adb6b512..fcffc44eefb834 100644 --- a/homeassistant/components/demo/mailbox.py +++ b/homeassistant/components/demo/mailbox.py @@ -63,7 +63,7 @@ async def async_get_media(self, msgid): raise StreamError("Message not found") audio_path = os.path.join( - os.path.dirname(__file__), '..', 'tts', 'demo.mp3') + os.path.dirname(__file__), 'tts.mp3') with open(audio_path, 'rb') as file: return file.read() diff --git a/homeassistant/components/tts/demo.mp3 b/homeassistant/components/demo/tts.mp3 similarity index 100% rename from homeassistant/components/tts/demo.mp3 rename to homeassistant/components/demo/tts.mp3 diff --git a/homeassistant/components/tts/demo.py b/homeassistant/components/demo/tts.py similarity index 90% rename from homeassistant/components/tts/demo.py rename to homeassistant/components/demo/tts.py index 6784e7cea61c4e..1498472ef9f29c 100644 --- a/homeassistant/components/tts/demo.py +++ b/homeassistant/components/demo/tts.py @@ -8,7 +8,7 @@ import voluptuous as vol -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider SUPPORT_LANGUAGES = [ 'en', 'de' @@ -51,7 +51,7 @@ def supported_options(self): def get_tts_audio(self, message, language, options=None): """Load TTS from demo.""" - filename = os.path.join(os.path.dirname(__file__), 'demo.mp3') + filename = os.path.join(os.path.dirname(__file__), 'tts.mp3') try: with open(filename, 'rb') as voice: data = voice.read() diff --git a/homeassistant/components/tts/marytts.py b/homeassistant/components/marytts/tts.py similarity index 97% rename from homeassistant/components/tts/marytts.py rename to homeassistant/components/marytts/tts.py index 971d3fb5705d7b..8f6a46b0c3ebdb 100644 --- a/homeassistant/components/tts/marytts.py +++ b/homeassistant/components/marytts/tts.py @@ -16,7 +16,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/microsoft.py b/homeassistant/components/microsoft/tts.py similarity index 100% rename from homeassistant/components/tts/microsoft.py rename to homeassistant/components/microsoft/tts.py diff --git a/homeassistant/components/tts/picotts.py b/homeassistant/components/picotts/tts.py similarity index 100% rename from homeassistant/components/tts/picotts.py rename to homeassistant/components/picotts/tts.py diff --git a/homeassistant/components/tts/voicerss.py b/homeassistant/components/voicerss/tts.py similarity index 98% rename from homeassistant/components/tts/voicerss.py rename to homeassistant/components/voicerss/tts.py index 3676dff3bc641b..20e0ee11db3928 100644 --- a/homeassistant/components/tts/voicerss.py +++ b/homeassistant/components/voicerss/tts.py @@ -15,7 +15,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tts/yandextts.py b/homeassistant/components/yandextts/tts.py similarity index 98% rename from homeassistant/components/tts/yandextts.py rename to homeassistant/components/yandextts/tts.py index aecba2925ddfe6..281839a2d74cb6 100644 --- a/homeassistant/components/tts/yandextts.py +++ b/homeassistant/components/yandextts/tts.py @@ -15,7 +15,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 8ad56528f4f0ae..f726f57471b8d4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -191,9 +191,6 @@ av==6.1.2 # homeassistant.components.axis axis==17 -# homeassistant.components.tts.baidu -baidu-aip==1.6.6 - # homeassistant.components.modem_callerid.sensor basicmodem==0.7 @@ -236,7 +233,6 @@ blockchain==1.4.4 # homeassistant.components.notify.aws_lambda # homeassistant.components.notify.aws_sns # homeassistant.components.notify.aws_sqs -# homeassistant.components.tts.amazon_polly boto3==1.9.16 # homeassistant.scripts.credstash @@ -991,9 +987,6 @@ pycomfoconnect==0.3 # homeassistant.components.coolmaster.climate pycoolmasternet==0.0.4 -# homeassistant.components.tts.microsoft -pycsspeechtts==1.0.2 - # homeassistant.components.cups.sensor # pycups==1.9.73 diff --git a/tests/components/marytts/__init__.py b/tests/components/marytts/__init__.py new file mode 100644 index 00000000000000..061776a1398697 --- /dev/null +++ b/tests/components/marytts/__init__.py @@ -0,0 +1 @@ +"""The tests for marytts tts platforms.""" diff --git a/tests/components/tts/test_marytts.py b/tests/components/marytts/test_tts.py similarity index 98% rename from tests/components/tts/test_marytts.py rename to tests/components/marytts/test_tts.py index 7520ba2fbaad8e..24915dd85c8dcf 100644 --- a/tests/components/tts/test_marytts.py +++ b/tests/components/marytts/test_tts.py @@ -11,7 +11,7 @@ from tests.common import ( get_test_home_assistant, assert_setup_component, mock_service) -from .test_init import mutagen_mock # noqa +from tests.components.tts.test_init import mutagen_mock # noqa class TestTTSMaryTTSPlatform: diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 4786370f24f655..140a938201bc35 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -9,7 +9,7 @@ import homeassistant.components.http as http import homeassistant.components.tts as tts -from homeassistant.components.tts.demo import DemoProvider +from homeassistant.components.demo.tts import DemoProvider from homeassistant.components.media_player.const import ( SERVICE_PLAY_MEDIA, MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP) @@ -229,7 +229,7 @@ def test_setup_component_and_test_service_with_service_options(self): "265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format( opt_hash))) - @patch('homeassistant.components.tts.demo.DemoProvider.default_options', + @patch('homeassistant.components.demo.tts.DemoProvider.default_options', new_callable=PropertyMock(return_value={'voice': 'alex'})) def test_setup_component_and_test_with_service_options_def(self, def_mock): """Set up the demo platform and call service with default options.""" @@ -519,7 +519,7 @@ def test_setup_component_test_with_cache_dir(self): with assert_setup_component(1, tts.DOMAIN): setup_component(self.hass, tts.DOMAIN, config) - with patch('homeassistant.components.tts.demo.DemoProvider.' + with patch('homeassistant.components.demo.tts.DemoProvider.' 'get_tts_audio', return_value=(None, None)): self.hass.services.call(tts.DOMAIN, 'demo_say', { tts.ATTR_MESSAGE: "I person is on front of your door.", @@ -531,7 +531,7 @@ def test_setup_component_test_with_cache_dir(self): "{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd" \ "_en_-_demo.mp3".format(self.hass.config.api.base_url) - @patch('homeassistant.components.tts.demo.DemoProvider.get_tts_audio', + @patch('homeassistant.components.demo.tts.DemoProvider.get_tts_audio', return_value=(None, None)) def test_setup_component_test_with_error_on_get_tts(self, tts_mock): """Set up demo platform with wrong get_tts_audio.""" diff --git a/tests/components/voicerss/__init__.py b/tests/components/voicerss/__init__.py new file mode 100644 index 00000000000000..9c037a14465bbe --- /dev/null +++ b/tests/components/voicerss/__init__.py @@ -0,0 +1 @@ +"""The tests for VoiceRSS tts platforms.""" diff --git a/tests/components/tts/test_voicerss.py b/tests/components/voicerss/test_tts.py similarity index 99% rename from tests/components/tts/test_voicerss.py rename to tests/components/voicerss/test_tts.py index af4bdf3976c39d..cd0e20cb9fab3a 100644 --- a/tests/components/tts/test_voicerss.py +++ b/tests/components/voicerss/test_tts.py @@ -11,7 +11,7 @@ from tests.common import ( get_test_home_assistant, assert_setup_component, mock_service) -from .test_init import mutagen_mock # noqa +from tests.components.tts.test_init import mutagen_mock # noqa class TestTTSVoiceRSSPlatform: diff --git a/tests/components/yandextts/__init__.py b/tests/components/yandextts/__init__.py new file mode 100644 index 00000000000000..54968b3605fa88 --- /dev/null +++ b/tests/components/yandextts/__init__.py @@ -0,0 +1 @@ +"""The tests for YandexTTS tts platforms.""" diff --git a/tests/components/tts/test_yandextts.py b/tests/components/yandextts/test_tts.py similarity index 99% rename from tests/components/tts/test_yandextts.py rename to tests/components/yandextts/test_tts.py index 70c75b1f2ed471..dd382271338cc6 100644 --- a/tests/components/tts/test_yandextts.py +++ b/tests/components/yandextts/test_tts.py @@ -10,7 +10,7 @@ from tests.common import ( get_test_home_assistant, assert_setup_component, mock_service) -from .test_init import mutagen_mock # noqa +from tests.components.tts.test_init import mutagen_mock # noqa class TestTTSYandexPlatform: From 3fddf5df08d6a3afd1b8d8cc7c88d643ec0d4050 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 26 Mar 2019 15:38:25 +0100 Subject: [PATCH 165/605] Enable hass.io panel without ping (#22388) * Enable hass.io panel without ping * fix tests --- homeassistant/components/hassio/__init__.py | 3 +-- homeassistant/components/hassio/handler.py | 2 +- tests/components/hassio/test_init.py | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 90e120d8b0eafe..073974200a0935 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -145,8 +145,7 @@ async def async_setup(hass, config): hass.data[DOMAIN] = hassio = HassIO(hass.loop, websession, host) if not await hassio.is_connected(): - _LOGGER.error("Not connected with Hass.io") - return False + _LOGGER.warning("Not connected with Hass.io / system to busy!") store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) data = await store.async_load() diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 7eb3245c0df5d6..7eddc639690a02 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -62,7 +62,7 @@ def is_connected(self): This method return a coroutine. """ - return self.send_command("/supervisor/ping", method="get") + return self.send_command("/supervisor/ping", method="get", timeout=15) @_api_data def get_homeassistant_info(self): diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index ba642b698f7afb..0c651aa0c5a9aa 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -219,15 +219,16 @@ def test_fail_setup_without_environ_var(hass): @asyncio.coroutine -def test_fail_setup_cannot_connect(hass): +def test_fail_setup_cannot_connect(hass, caplog): """Fail setup if cannot connect.""" with patch.dict(os.environ, MOCK_ENVIRON), \ patch('homeassistant.components.hassio.HassIO.is_connected', Mock(return_value=mock_coro(None))): result = yield from async_setup_component(hass, 'hassio', {}) - assert not result + assert result - assert not hass.components.hassio.is_hassio() + assert hass.components.hassio.is_hassio() + assert "Not connected with Hass.io / system to busy!" in caplog.text @asyncio.coroutine From 133ae63ed08480cb34d8a77546589d1a220da56e Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 26 Mar 2019 14:39:05 +0000 Subject: [PATCH 166/605] Add missing append (#22414) --- homeassistant/components/homekit_controller/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 5e470e1540baa6..0ed208af979e12 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -102,6 +102,7 @@ def accessory_setup(self): if component is not None: discovery.load_platform(self.hass, component, DOMAIN, service_info, self.config) + self.entities.append((aid, iid)) def device_config_callback(self, callback_data): """Handle initial pairing.""" From 4d0a28fa56e9ae1fe89eaf265aa226724bcb605c Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Sun, 24 Mar 2019 17:08:59 -0700 Subject: [PATCH 167/605] Fix ps4 no creds with additional device (#22300) * Fix no creds with additional device. * Update config_flow.py --- homeassistant/components/ps4/config_flow.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/homeassistant/components/ps4/config_flow.py b/homeassistant/components/ps4/config_flow.py index d000ed1f7e7eef..e0b41dfadd575f 100644 --- a/homeassistant/components/ps4/config_flow.py +++ b/homeassistant/components/ps4/config_flow.py @@ -79,7 +79,11 @@ async def async_step_link(self, user_input=None): # If entry exists check that devices found aren't configured. if self.hass.config_entries.async_entries(DOMAIN): + creds = {} for entry in self.hass.config_entries.async_entries(DOMAIN): + # Retrieve creds from entry + creds['data'] = entry.data[CONF_TOKEN] + # Retrieve device data from entry conf_devices = entry.data['devices'] for c_device in conf_devices: if c_device['host'] in device_list: @@ -88,6 +92,11 @@ async def async_step_link(self, user_input=None): # If list is empty then all devices are configured. if not device_list: return self.async_abort(reason='devices_configured') + # Add existing creds for linking. Should be only 1. + if not creds: + # Abort if creds is missing. + return self.async_abort(reason='credential_error') + self.creds = creds['data'] # Login to PS4 with user data. if user_input is not None: From 24a55834f9ce4d9e22c25a3eb6d3216587034c71 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 24 Mar 2019 20:13:20 -0400 Subject: [PATCH 168/605] Prefer TCP for RTSP streams (#22338) ## Description: For RTSP streams, set the `prefer_tcp` FFMPEG flag. This should resolve some of the "green feed" issues that some users are reporting, likely due to packets being lost over UDP on their network. Resources: [FFMPEG protocols documentation](https://ffmpeg.org/ffmpeg-protocols.html#rtsp) ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/stream/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 3f715af0e047d4..c881ec1276a96f 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -44,6 +44,11 @@ def request_stream(hass, stream_source, *, fmt='hls', if options is None: options = {} + # For RTSP streams, prefer TCP + if isinstance(stream_source, str) \ + and stream_source[:7] == 'rtsp://' and not options: + options['rtsp_flags'] = 'prefer_tcp' + try: streams = hass.data[DOMAIN][ATTR_STREAMS] stream = streams.get(stream_source) From a2508b4f52fcc75156795003162e4a336cec9e58 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 25 Mar 2019 17:43:15 +0100 Subject: [PATCH 169/605] Update hass-nabucasa & fix state (#22385) * Update hass-nabucasa & fix state * Fix lint --- homeassistant/components/cloud/__init__.py | 2 +- homeassistant/components/cloud/binary_sensor.py | 12 ++++++++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_binary_sensor.py | 3 +++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 9517971b16d8d1..76a768385f8508 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,7 +24,7 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.10'] +REQUIREMENTS = ['hass-nabucasa==0.11'] DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/cloud/binary_sensor.py b/homeassistant/components/cloud/binary_sensor.py index 874c3420c5844e..19a6528e3218f5 100644 --- a/homeassistant/components/cloud/binary_sensor.py +++ b/homeassistant/components/cloud/binary_sensor.py @@ -1,6 +1,7 @@ """Support for Home Assistant Cloud binary sensors.""" +import asyncio + from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN @@ -8,6 +9,9 @@ DEPENDENCIES = ['cloud'] +WAIT_UNTIL_CHANGE = 3 + + async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): """Set up the cloud binary sensors.""" @@ -58,10 +62,10 @@ def should_poll(self) -> bool: async def async_added_to_hass(self): """Register update dispatcher.""" - @callback - def async_state_update(data): + async def async_state_update(data): """Update callback.""" - self.async_write_ha_state() + await asyncio.sleep(WAIT_UNTIL_CHANGE) + self.async_schedule_update_ha_state() self._unsub_dispatcher = async_dispatcher_connect( self.hass, DISPATCHER_REMOTE_UPDATE, async_state_update) diff --git a/requirements_all.txt b/requirements_all.txt index 45d9386cbd8cba..793cc4796b499f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -524,7 +524,7 @@ habitipy==0.2.0 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.10 +hass-nabucasa==0.11 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3340bb40d9cf31..fcaf93701fe242 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ ha-ffmpeg==1.11 hangups==0.4.6 # homeassistant.components.cloud -hass-nabucasa==0.10 +hass-nabucasa==0.11 # homeassistant.components.mqtt.server hbmqtt==0.9.4 diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py index 938829b809bdb6..f6d8783a609a71 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -7,6 +7,9 @@ async def test_remote_connection_sensor(hass): """Test the remote connection sensor.""" + from homeassistant.components.cloud import binary_sensor as bin_sensor + bin_sensor.WAIT_UNTIL_CHANGE = 0 + assert await async_setup_component(hass, 'cloud', {'cloud': {}}) cloud = hass.data['cloud'] = Mock() cloud.remote.certificate = None From fac214828d710353ecd3c53be0237a938cf77157 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 26 Mar 2019 15:38:25 +0100 Subject: [PATCH 170/605] Enable hass.io panel without ping (#22388) * Enable hass.io panel without ping * fix tests --- homeassistant/components/hassio/__init__.py | 3 +-- homeassistant/components/hassio/handler.py | 2 +- tests/components/hassio/test_init.py | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 7f85c8cfc3fc66..f89a6539cd0fc7 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -145,8 +145,7 @@ async def async_setup(hass, config): hass.data[DOMAIN] = hassio = HassIO(hass.loop, websession, host) if not await hassio.is_connected(): - _LOGGER.error("Not connected with Hass.io") - return False + _LOGGER.warning("Not connected with Hass.io / system to busy!") store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) data = await store.async_load() diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 7eb3245c0df5d6..7eddc639690a02 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -62,7 +62,7 @@ def is_connected(self): This method return a coroutine. """ - return self.send_command("/supervisor/ping", method="get") + return self.send_command("/supervisor/ping", method="get", timeout=15) @_api_data def get_homeassistant_info(self): diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 1326805fc93944..dafb8f1a028d12 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -196,15 +196,16 @@ def test_fail_setup_without_environ_var(hass): @asyncio.coroutine -def test_fail_setup_cannot_connect(hass): +def test_fail_setup_cannot_connect(hass, caplog): """Fail setup if cannot connect.""" with patch.dict(os.environ, MOCK_ENVIRON), \ patch('homeassistant.components.hassio.HassIO.is_connected', Mock(return_value=mock_coro(None))): result = yield from async_setup_component(hass, 'hassio', {}) - assert not result + assert result - assert not hass.components.hassio.is_hassio() + assert hass.components.hassio.is_hassio() + assert "Not connected with Hass.io / system to busy!" in caplog.text @asyncio.coroutine From 8a6d9cc0e428f22a70e1ade1639dca2a62102548 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 26 Mar 2019 03:39:09 -0400 Subject: [PATCH 171/605] reset unsub to None on timeout (#22404) --- homeassistant/components/stream/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 665803d38ebcab..59c0a6b650fd46 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -128,6 +128,7 @@ def put(self, segment: Segment) -> None: @callback def _timeout(self, _now=None): """Handle stream timeout.""" + self._unsub = None if self._stream.keepalive: self.idle = True self._stream.check_idle() From ab1f1316ba80ff7fe73f981f3a8e3527eaac9279 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 26 Mar 2019 07:40:10 -0700 Subject: [PATCH 172/605] Bumped version to 0.90.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ba33a566c9ac52..d8bd84a079a884 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 90 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 7519e8d4175659607cf113171943d80024eee49e Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 26 Mar 2019 07:48:26 -0700 Subject: [PATCH 173/605] Update translate, fix dev build error (#22419) --- .../components/axis/.translations/en.json | 30 +++++++++---------- .../components/ps4/.translations/en.json | 22 +++++++------- homeassistant/components/ps4/strings.json | 2 +- .../tellduslive/.translations/en.json | 1 - .../components/upnp/.translations/en.json | 15 ---------- .../components/zha/.translations/en.json | 1 + 6 files changed, 28 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/axis/.translations/en.json b/homeassistant/components/axis/.translations/en.json index 3c528dfbb16112..6c5933dfd97263 100644 --- a/homeassistant/components/axis/.translations/en.json +++ b/homeassistant/components/axis/.translations/en.json @@ -1,26 +1,26 @@ { "config": { - "title": "Axis device", + "abort": { + "already_configured": "Device is already configured", + "bad_config_file": "Bad data from config file", + "link_local_address": "Link local addresses are not supported" + }, + "error": { + "already_configured": "Device is already configured", + "device_unavailable": "Device is not available", + "faulty_credentials": "Bad user credentials" + }, "step": { "user": { - "title": "Set up Axis device", "data": { "host": "Host", - "username": "Username", "password": "Password", - "port": "Port" - } + "port": "Port", + "username": "Username" + }, + "title": "Set up Axis device" } }, - "error": { - "already_configured": "Device is already configured", - "device_unavailable": "Device is not available", - "faulty_credentials": "Bad user credentials" - }, - "abort": { - "already_configured": "Device is already configured", - "bad_config_file": "Bad data from config file", - "link_local_address": "Link local addresses are not supported" - } + "title": "Axis device" } } \ No newline at end of file diff --git a/homeassistant/components/ps4/.translations/en.json b/homeassistant/components/ps4/.translations/en.json index 662f6fb6116d75..8949e77b4cceff 100644 --- a/homeassistant/components/ps4/.translations/en.json +++ b/homeassistant/components/ps4/.translations/en.json @@ -9,22 +9,14 @@ }, "error": { "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.", - "not_ready": "PlayStation 4 is not on or connected to network.", - "no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure." + "no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure.", + "not_ready": "PlayStation 4 is not on or connected to network." }, "step": { "creds": { "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue.", "title": "PlayStation 4" }, - "mode": { - "data": { - "mode": "Config Mode", - "ip_address": "IP Address (Leave empty if using Auto Discovery)." - }, - "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", - "title": "PlayStation 4" - }, "link": { "data": { "code": "PIN", @@ -34,8 +26,16 @@ }, "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP Address (Leave empty if using Auto Discovery).", + "mode": "Config Mode" + }, + "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" } -} +} \ No newline at end of file diff --git a/homeassistant/components/ps4/strings.json b/homeassistant/components/ps4/strings.json index d8fdc9e18dbcd4..ea69d8c7a8c873 100644 --- a/homeassistant/components/ps4/strings.json +++ b/homeassistant/components/ps4/strings.json @@ -12,7 +12,7 @@ "data": { "mode": "Config Mode", "ip_address": "IP Address (Leave empty if using Auto Discovery)." - }, + } }, "link": { "title": "PlayStation 4", diff --git a/homeassistant/components/tellduslive/.translations/en.json b/homeassistant/components/tellduslive/.translations/en.json index c2b00561858746..4ed9ef597f489d 100644 --- a/homeassistant/components/tellduslive/.translations/en.json +++ b/homeassistant/components/tellduslive/.translations/en.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "all_configured": "TelldusLive is already configured", "already_setup": "TelldusLive is already configured", "authorize_url_fail": "Unknown error generating an authorize url.", "authorize_url_timeout": "Timeout generating authorize url.", diff --git a/homeassistant/components/upnp/.translations/en.json b/homeassistant/components/upnp/.translations/en.json index 632d5112f1ae2e..91e4f6b7c52574 100644 --- a/homeassistant/components/upnp/.translations/en.json +++ b/homeassistant/components/upnp/.translations/en.json @@ -1,28 +1,13 @@ { "config": { "abort": { - "already_configured": "UPnP/IGD is already configured", - "incomplete_device": "Ignoring incomplete UPnP device", - "no_devices_discovered": "No UPnP/IGDs discovered", "no_devices_found": "No UPnP/IGD devices found on the network.", - "no_sensors_or_port_mapping": "Enable at least sensors or port mapping", "single_instance_allowed": "Only a single configuration of UPnP/IGD is necessary." }, "step": { "confirm": { "description": "Do you want to set up UPnP/IGD?", "title": "UPnP/IGD" - }, - "init": { - "title": "UPnP/IGD" - }, - "user": { - "data": { - "enable_port_mapping": "Enable port mapping for Home Assistant", - "enable_sensors": "Add traffic sensors", - "igd": "UPnP/IGD" - }, - "title": "Configuration options for the UPnP/IGD" } }, "title": "UPnP/IGD" diff --git a/homeassistant/components/zha/.translations/en.json b/homeassistant/components/zha/.translations/en.json index f0da251f5eb643..82489ac258ec14 100644 --- a/homeassistant/components/zha/.translations/en.json +++ b/homeassistant/components/zha/.translations/en.json @@ -12,6 +12,7 @@ "radio_type": "Radio Type", "usb_path": "USB Device Path" }, + "description": "", "title": "ZHA" } }, From afa99c91893957b60df80bf83bd306a1dc7cdd43 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 26 Mar 2019 16:06:11 +0100 Subject: [PATCH 174/605] Use dispatcher for netgear_lte state updates (#22328) * Use dispatcher for netgear_lte state updates * Also dispatch unavailable state --- .../components/netgear_lte/__init__.py | 18 ++++++++++++++---- homeassistant/components/netgear_lte/sensor.py | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index 34330426e34232..730c3851a2dea5 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -15,7 +15,8 @@ from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_create_clientsession -from homeassistant.util import Throttle +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.event import async_track_time_interval from . import sensor_types @@ -23,7 +24,8 @@ _LOGGER = logging.getLogger(__name__) -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) +SCAN_INTERVAL = timedelta(seconds=10) +DISPATCHER_NETGEAR_LTE = 'netgear_lte_update' DOMAIN = 'netgear_lte' DATA_KEY = 'netgear_lte' @@ -56,6 +58,7 @@ class ModemData: """Class for modem state.""" + hass = attr.ib() host = attr.ib() modem = attr.ib() @@ -64,7 +67,6 @@ class ModemData: usage = attr.ib(init=False, default=None) connected = attr.ib(init=False, default=True) - @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self): """Call the API to update the data.""" import eternalegypt @@ -83,6 +85,8 @@ async def async_update(self): self.unread_count = None self.usage = None + async_dispatcher_send(self.hass, DISPATCHER_NETGEAR_LTE) + @attr.s class LTEData: @@ -143,7 +147,7 @@ async def _setup_lte(hass, lte_config): websession = hass.data[DATA_KEY].websession modem = eternalegypt.Modem(hostname=host, websession=websession) - modem_data = ModemData(host, modem) + modem_data = ModemData(hass, host, modem) try: await _login(hass, modem_data, password) @@ -172,6 +176,12 @@ async def cleanup(event): hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, cleanup) + async def _update(now): + """Periodic update.""" + await modem_data.async_update() + + async_track_time_interval(hass, _update, SCAN_INTERVAL) + async def _retry_login(hass, modem_data, password): """Sleep and retry setup.""" diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 774cdc5536a1fd..a13f5fbfaa7749 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -6,8 +6,9 @@ from homeassistant.components.sensor import DOMAIN from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity import Entity +from homeassistant.helpers.dispatcher import async_dispatcher_connect -from . import CONF_MONITORED_CONDITIONS, DATA_KEY +from . import CONF_MONITORED_CONDITIONS, DATA_KEY, DISPATCHER_NETGEAR_LTE from .sensor_types import SENSOR_SMS, SENSOR_USAGE DEPENDENCIES = ['netgear_lte'] @@ -36,7 +37,7 @@ async def async_setup_platform( elif sensor_type == SENSOR_USAGE: sensors.append(UsageSensor(modem_data, sensor_type)) - async_add_entities(sensors, True) + async_add_entities(sensors) @attr.s @@ -46,10 +47,20 @@ class LTESensor(Entity): modem_data = attr.ib() sensor_type = attr.ib() + async def async_added_to_hass(self): + """Register callback.""" + async_dispatcher_connect( + self.hass, DISPATCHER_NETGEAR_LTE, self.async_write_ha_state) + async def async_update(self): - """Update state.""" + """Force update of state.""" await self.modem_data.async_update() + @property + def should_poll(self): + """Return that the sensor should not be polled.""" + return False + @property def unique_id(self): """Return a unique ID like 'usage_5TG365AB0078V'.""" From 7e3567319f767765dd899af8b01e64e6f757b247 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Tue, 26 Mar 2019 20:13:56 +0000 Subject: [PATCH 175/605] ciscomobilityexpress pypi version update (#22431) * Bump pip * Bump ciscomobilityexpress to 0.1.4 --- .../components/cisco_mobility_express/device_tracker.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cisco_mobility_express/device_tracker.py b/homeassistant/components/cisco_mobility_express/device_tracker.py index 60f8761aeeb909..c5e2b4284d32a0 100644 --- a/homeassistant/components/cisco_mobility_express/device_tracker.py +++ b/homeassistant/components/cisco_mobility_express/device_tracker.py @@ -10,7 +10,7 @@ CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_SSL, CONF_VERIFY_SSL) -REQUIREMENTS = ['ciscomobilityexpress==0.1.2'] +REQUIREMENTS = ['ciscomobilityexpress==0.1.4'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index f726f57471b8d4..fc8d7357d9231e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -268,7 +268,7 @@ buienradar==0.91 caldav==0.5.0 # homeassistant.components.cisco_mobility_express.device_tracker -ciscomobilityexpress==0.1.2 +ciscomobilityexpress==0.1.4 # homeassistant.components.notify.ciscospark ciscosparkapi==0.4.2 From 44dc85204c48e259a610b973ebe8a87659bc544c Mon Sep 17 00:00:00 2001 From: mvn23 Date: Thu, 14 Mar 2019 13:11:04 +0100 Subject: [PATCH 176/605] Bump pyotgw to 0.4b2 (#21973) --- homeassistant/components/opentherm_gw/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 7676806cfdfb94..d66059c55a04d8 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyotgw==0.4b1'] +REQUIREMENTS = ['pyotgw==0.4b2'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 793cc4796b499f..814f80c1597401 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1205,7 +1205,7 @@ pyoppleio==1.0.5 pyota==2.0.5 # homeassistant.components.opentherm_gw -pyotgw==0.4b1 +pyotgw==0.4b2 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp From 42e75dc45c292c0d3c1ca307ac140830e2e667a8 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 16:32:51 +0100 Subject: [PATCH 177/605] Fix opentherm_gw blocks HA startup when gateway unreachable. (#22106) --- homeassistant/components/opentherm_gw/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index d66059c55a04d8..4fa24604edc925 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -8,8 +8,9 @@ from homeassistant.components.sensor import DOMAIN as COMP_SENSOR from homeassistant.const import ( ATTR_DATE, ATTR_ID, ATTR_TEMPERATURE, ATTR_TIME, CONF_DEVICE, - CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_STOP, - PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE) + CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP, PRECISION_HALVES, PRECISION_TENTHS, + PRECISION_WHOLE) from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -115,14 +116,18 @@ async def async_setup(hass, config): DATA_GW_VARS: pyotgw.vars, DATA_LATEST_STATUS: {} } - hass.async_create_task(connect_and_subscribe( - hass, conf[CONF_DEVICE], gateway)) hass.async_create_task(register_services(hass, gateway)) hass.async_create_task(async_load_platform( hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE), config)) if monitored_vars: hass.async_create_task(setup_monitored_vars( hass, config, monitored_vars)) + + def schedule_connect(event): + """Schedule the connect_and_subscribe coroutine.""" + hass.async_create_task( + connect_and_subscribe(hass, conf[CONF_DEVICE], gateway)) + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_connect) return True From ddefb7421509d170d201ece7a007d25894e42ebb Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 16:34:31 +0100 Subject: [PATCH 178/605] Fix TypeError in current_temperature if no temperature is known. (#22112) Don't set opentherm_gw climate temperatures to 0 on init. --- homeassistant/components/opentherm_gw/climate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 584be4c0c64834..1a7c031638f79f 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -37,8 +37,8 @@ def __init__(self, hass, config): self.floor_temp = config.get(CONF_FLOOR_TEMP) self.temp_precision = config.get(CONF_PRECISION) self._current_operation = STATE_IDLE - self._current_temperature = 0.0 - self._target_temperature = 0.0 + self._current_temperature = None + self._target_temperature = None self._away_mode_a = None self._away_mode_b = None self._away_state_a = False @@ -124,6 +124,8 @@ def current_operation(self): @property def current_temperature(self): """Return the current temperature.""" + if self._current_temperature is None: + return if self.floor_temp is True: if self.temp_precision == PRECISION_HALVES: return int(2 * self._current_temperature) / 2 From 24ba434e6a271b16e9164179bb2e7ff90e2fadb7 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 16 Mar 2019 23:51:50 +0100 Subject: [PATCH 179/605] Improve opentherm gw startup (#22121) * Improve fix in c90f0d5 (#22106). Schedule connect coroutine directly on the loop rather than waiting for EVENT_HOMEASSISTANT_START. * Remove unused import. --- homeassistant/components/opentherm_gw/__init__.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 4fa24604edc925..acb277c0ef5d0e 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -8,9 +8,8 @@ from homeassistant.components.sensor import DOMAIN as COMP_SENSOR from homeassistant.const import ( ATTR_DATE, ATTR_ID, ATTR_TEMPERATURE, ATTR_TIME, CONF_DEVICE, - CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, PRECISION_HALVES, PRECISION_TENTHS, - PRECISION_WHOLE) + CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_STOP, + PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE) from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -122,12 +121,9 @@ async def async_setup(hass, config): if monitored_vars: hass.async_create_task(setup_monitored_vars( hass, config, monitored_vars)) - - def schedule_connect(event): - """Schedule the connect_and_subscribe coroutine.""" - hass.async_create_task( - connect_and_subscribe(hass, conf[CONF_DEVICE], gateway)) - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_connect) + # Schedule directly on the loop to avoid blocking HA startup. + hass.loop.create_task( + connect_and_subscribe(hass, conf[CONF_DEVICE], gateway)) return True From 80250add9e80f53a5e92bca5c7ac2dbaf06319c6 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 26 Mar 2019 22:42:43 +0100 Subject: [PATCH 180/605] Update homeassistant-pyozw to 0.1.3 (#22433) --- homeassistant/components/zwave/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 604f03387bd6ec..4abaaa312109b8 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -37,7 +37,7 @@ from .util import (check_node_schema, check_value_schema, node_name, check_has_unique_id, is_node_parsed) -REQUIREMENTS = ['pydispatcher==2.0.5', 'homeassistant-pyozw==0.1.2'] +REQUIREMENTS = ['pydispatcher==2.0.5', 'homeassistant-pyozw==0.1.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index fc8d7357d9231e..a5642a989d7887 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -550,7 +550,7 @@ holidays==0.9.10 home-assistant-frontend==20190321.0 # homeassistant.components.zwave -homeassistant-pyozw==0.1.2 +homeassistant-pyozw==0.1.3 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 02b12ec1b9cfd6246095e09af286d3a5c65e9a90 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Tue, 26 Mar 2019 21:49:53 +0000 Subject: [PATCH 181/605] Adding conf for deep standby, wake and specific source bouquet of Enigma2 (#22393) * - adding deep standby, conf for wake and specify source bouquet * set defaults to strings * bump pip * bump pip * bump pip * bump pip * bump pip * bump pip --- .../components/enigma2/media_player.py | 20 +++++++++++++++++-- requirements_all.txt | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/enigma2/media_player.py b/homeassistant/components/enigma2/media_player.py index 40101120f129d9..0b6f995be97123 100644 --- a/homeassistant/components/enigma2/media_player.py +++ b/homeassistant/components/enigma2/media_player.py @@ -14,7 +14,7 @@ STATE_OFF, STATE_ON, STATE_PLAYING, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['openwebifpy==1.2.7'] +REQUIREMENTS = ['openwebifpy==3.1.0'] _LOGGER = logging.getLogger(__name__) @@ -24,6 +24,9 @@ ATTR_MEDIA_START_TIME = 'media_start_time' CONF_USE_CHANNEL_ICON = "use_channel_icon" +CONF_DEEP_STANDBY = "deep_standby" +CONF_MAC_ADDRESS = "mac_address" +CONF_SOURCE_BOUQUET = "source_bouquet" DEFAULT_NAME = 'Enigma2 Media Player' DEFAULT_PORT = 80 @@ -31,6 +34,9 @@ DEFAULT_USE_CHANNEL_ICON = False DEFAULT_USERNAME = 'root' DEFAULT_PASSWORD = 'dreambox' +DEFAULT_DEEP_STANDBY = False +DEFAULT_MAC_ADDRESS = '' +DEFAULT_SOURCE_BOUQUET = '' SUPPORTED_ENIGMA2 = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_OFF | SUPPORT_NEXT_TRACK | SUPPORT_STOP | \ @@ -46,6 +52,10 @@ vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, vol.Optional(CONF_USE_CHANNEL_ICON, default=DEFAULT_USE_CHANNEL_ICON): cv.boolean, + vol.Optional(CONF_DEEP_STANDBY, default=DEFAULT_DEEP_STANDBY): cv.boolean, + vol.Optional(CONF_MAC_ADDRESS, default=DEFAULT_MAC_ADDRESS): cv.string, + vol.Optional(CONF_SOURCE_BOUQUET, + default=DEFAULT_SOURCE_BOUQUET): cv.string, }) @@ -62,6 +72,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): config[CONF_PASSWORD] = DEFAULT_PASSWORD config[CONF_SSL] = DEFAULT_SSL config[CONF_USE_CHANNEL_ICON] = DEFAULT_USE_CHANNEL_ICON + config[CONF_MAC_ADDRESS] = DEFAULT_MAC_ADDRESS + config[CONF_DEEP_STANDBY] = DEFAULT_DEEP_STANDBY + config[CONF_SOURCE_BOUQUET] = DEFAULT_SOURCE_BOUQUET from openwebif.api import CreateDevice device = \ @@ -70,7 +83,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): username=config.get(CONF_USERNAME), password=config.get(CONF_PASSWORD), is_https=config.get(CONF_SSL), - prefer_picon=config.get(CONF_USE_CHANNEL_ICON)) + prefer_picon=config.get(CONF_USE_CHANNEL_ICON), + mac_address=config.get(CONF_MAC_ADDRESS), + turn_off_to_deep=config.get(CONF_DEEP_STANDBY), + source_bouquet=config.get(CONF_SOURCE_BOUQUET)) add_devices([Enigma2Device(config[CONF_NAME], device)], True) diff --git a/requirements_all.txt b/requirements_all.txt index a5642a989d7887..75e3ece960eef4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -785,7 +785,7 @@ openhomedevice==0.4.2 opensensemap-api==0.1.5 # homeassistant.components.enigma2.media_player -openwebifpy==1.2.7 +openwebifpy==3.1.0 # homeassistant.components.luci.device_tracker openwrt-luci-rpc==1.0.5 From 176653681259d2aa56449f8e37d89db8058c4355 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 26 Mar 2019 15:24:28 -0700 Subject: [PATCH 182/605] Fix test name (#22421) --- tests/components/hassio/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 0c651aa0c5a9aa..fc4661e7544bd9 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -219,8 +219,8 @@ def test_fail_setup_without_environ_var(hass): @asyncio.coroutine -def test_fail_setup_cannot_connect(hass, caplog): - """Fail setup if cannot connect.""" +def test_warn_when_cannot_connect(hass, caplog): + """Fail warn when we cannot connect.""" with patch.dict(os.environ, MOCK_ENVIRON), \ patch('homeassistant.components.hassio.HassIO.is_connected', Mock(return_value=mock_coro(None))): From 19d99ddf57578fc7daed3dd60bbbbaa07fcdb7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Wed, 27 Mar 2019 00:18:32 +0100 Subject: [PATCH 183/605] Lower severity level of log messages from http.view (#21091) --- homeassistant/components/http/view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index daac9fef74823e..d68cabebacf39f 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -102,8 +102,8 @@ async def handle(request): else: raise HTTPUnauthorized() - _LOGGER.info('Serving %s to %s (auth: %s)', - request.path, request.get(KEY_REAL_IP), authenticated) + _LOGGER.debug('Serving %s to %s (auth: %s)', + request.path, request.get(KEY_REAL_IP), authenticated) try: result = handler(request, **request.match_info) From a55afa8119dd7ff6f70d10fbcec7f73bd7176a93 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 27 Mar 2019 07:55:05 +0100 Subject: [PATCH 184/605] Update ha-ffmpeg 2.0 (#22427) --- homeassistant/components/amcrest/camera.py | 5 +++-- homeassistant/components/arlo/camera.py | 5 +++-- homeassistant/components/canary/camera.py | 7 ++++--- homeassistant/components/ffmpeg/__init__.py | 2 +- homeassistant/components/ffmpeg/camera.py | 7 ++++--- .../components/ffmpeg_motion/binary_sensor.py | 2 +- homeassistant/components/ffmpeg_noise/binary_sensor.py | 2 +- homeassistant/components/onvif/camera.py | 7 ++++--- homeassistant/components/ring/camera.py | 7 ++++--- homeassistant/components/xiaomi/camera.py | 7 ++++--- homeassistant/components/yi/camera.py | 7 ++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../test_ffmpeg.py => ffmpeg/test_sensor.py} | 10 ++++++---- 14 files changed, 41 insertions(+), 31 deletions(-) rename tests/components/{binary_sensor/test_ffmpeg.py => ffmpeg/test_sensor.py} (94%) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 7ba3ea04bf5563..35d5e18fdd3505 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -78,7 +78,7 @@ async def handle_async_mjpeg_stream(self, request): self.hass, request, stream_coro) # streaming via ffmpeg - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg streaming_url = self._camera.rtsp_url(typeno=self._resolution) stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) @@ -86,8 +86,9 @@ async def handle_async_mjpeg_stream(self, request): streaming_url, extra_cmd=self._ffmpeg_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._ffmpeg.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/arlo/camera.py b/homeassistant/components/arlo/camera.py index 43ccabb7390b98..95d11318bf7cbf 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -83,7 +83,7 @@ def _update_callback(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg video = self._camera.last_video if not video: error_msg = \ @@ -97,8 +97,9 @@ async def handle_async_mjpeg_stream(self, request): video.video_url, extra_cmd=self._ffmpeg_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._ffmpeg.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index c54565d6fde990..63c27d31d9339c 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -80,7 +80,7 @@ async def async_camera_image(self): """Return a still image response from the camera.""" self.renew_live_stream_session() - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop) image = await asyncio.shield(ffmpeg.get_image( self._live_stream_session.live_stream_url, @@ -93,15 +93,16 @@ async def handle_async_mjpeg_stream(self, request): if self._live_stream_session is None: return - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) await stream.open_camera( self._live_stream_session.live_stream_url, extra_cmd=self._ffmpeg_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._ffmpeg.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/ffmpeg/__init__.py b/homeassistant/components/ffmpeg/__init__.py index 7b7e3a81294088..05bc1d991678d0 100644 --- a/homeassistant/components/ffmpeg/__init__.py +++ b/homeassistant/components/ffmpeg/__init__.py @@ -12,7 +12,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['ha-ffmpeg==1.11'] +REQUIREMENTS = ['ha-ffmpeg==2.0'] DOMAIN = 'ffmpeg' diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index 4272a3d6029dd9..dbb51bf27c7caf 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -48,7 +48,7 @@ def __init__(self, hass, config): async def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop) image = await asyncio.shield(ffmpeg.get_image( @@ -58,15 +58,16 @@ async def async_camera_image(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop) await stream.open_camera( self._input, extra_cmd=self._extra_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._manager.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/ffmpeg_motion/binary_sensor.py b/homeassistant/components/ffmpeg_motion/binary_sensor.py index d0e597e13c01a4..8183b0e66a61a9 100644 --- a/homeassistant/components/ffmpeg_motion/binary_sensor.py +++ b/homeassistant/components/ffmpeg_motion/binary_sensor.py @@ -86,7 +86,7 @@ class FFmpegMotion(FFmpegBinarySensor): def __init__(self, hass, manager, config): """Initialize FFmpeg motion binary sensor.""" - from haffmpeg import SensorMotion + from haffmpeg.sensor import SensorMotion super().__init__(config) self.ffmpeg = SensorMotion( diff --git a/homeassistant/components/ffmpeg_noise/binary_sensor.py b/homeassistant/components/ffmpeg_noise/binary_sensor.py index 070c8c61b00be9..56edf1ddfd6a2e 100644 --- a/homeassistant/components/ffmpeg_noise/binary_sensor.py +++ b/homeassistant/components/ffmpeg_noise/binary_sensor.py @@ -55,7 +55,7 @@ class FFmpegNoise(FFmpegBinarySensor): def __init__(self, hass, manager, config): """Initialize FFmpeg noise binary sensor.""" - from haffmpeg import SensorNoise + from haffmpeg.sensor import SensorNoise super().__init__(config) self.ffmpeg = SensorNoise( diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index f3b25e3a1285a5..36f1b18eebf0e7 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -190,7 +190,7 @@ async def async_added_to_hass(self): async def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG if not self._input: await self.hass.async_add_job(self.obtain_input_uri) @@ -207,7 +207,7 @@ async def async_camera_image(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg if not self._input: await self.hass.async_add_job(self.obtain_input_uri) @@ -221,8 +221,9 @@ async def handle_async_mjpeg_stream(self, request): self._input, extra_cmd=self._ffmpeg_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, ffmpeg_manager.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/ring/camera.py b/homeassistant/components/ring/camera.py index efcdf8599a9bb8..8970e61b1a1fa0 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -115,7 +115,7 @@ def device_state_attributes(self): async def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop) if self._video_url is None: @@ -128,7 +128,7 @@ async def async_camera_image(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg if self._video_url is None: return @@ -138,8 +138,9 @@ async def handle_async_mjpeg_stream(self, request): self._video_url, extra_cmd=self._ffmpeg_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._ffmpeg.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index 93e9dd4a07c8e0..d8cd59129abb59 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -138,7 +138,7 @@ def get_latest_video_url(self): async def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG url = await self.hass.async_add_job(self.get_latest_video_url) if url != self._last_url: @@ -152,15 +152,16 @@ async def async_camera_image(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop) await stream.open_camera( self._last_url, extra_cmd=self._extra_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._manager.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/homeassistant/components/yi/camera.py b/homeassistant/components/yi/camera.py index 7d731d2a433975..c60d4971fb843c 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -118,7 +118,7 @@ async def _get_latest_video_url(self): async def async_camera_image(self): """Return a still image response from the camera.""" - from haffmpeg import ImageFrame, IMAGE_JPEG + from haffmpeg.tools import ImageFrame, IMAGE_JPEG url = await self._get_latest_video_url() if url and url != self._last_url: @@ -135,7 +135,7 @@ async def async_camera_image(self): async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" - from haffmpeg import CameraMjpeg + from haffmpeg.camera import CameraMjpeg if not self._is_on: return @@ -145,8 +145,9 @@ async def handle_async_mjpeg_stream(self, request): self._last_url, extra_cmd=self._extra_arguments) try: + stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( - self.hass, request, stream, + self.hass, request, stream_reader, self._manager.ffmpeg_stream_content_type) finally: await stream.close() diff --git a/requirements_all.txt b/requirements_all.txt index 75e3ece960eef4..4b4b2570d50c64 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -505,7 +505,7 @@ greenwavereality==0.5.1 gstreamer-player==1.1.2 # homeassistant.components.ffmpeg -ha-ffmpeg==1.11 +ha-ffmpeg==2.0 # homeassistant.components.philips_js.media_player ha-philipsjs==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 17c993bf5f7312..08466d922b86c6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -114,7 +114,7 @@ geojson_client==0.3 georss_client==0.5 # homeassistant.components.ffmpeg -ha-ffmpeg==1.11 +ha-ffmpeg==2.0 # homeassistant.components.hangouts hangups==0.4.6 diff --git a/tests/components/binary_sensor/test_ffmpeg.py b/tests/components/ffmpeg/test_sensor.py similarity index 94% rename from tests/components/binary_sensor/test_ffmpeg.py rename to tests/components/ffmpeg/test_sensor.py index 2c17207af321b9..d1fd6124b4cb4d 100644 --- a/tests/components/binary_sensor/test_ffmpeg.py +++ b/tests/components/ffmpeg/test_sensor.py @@ -33,7 +33,8 @@ def test_setup_component(self): assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_noise') is not None - @patch('haffmpeg.SensorNoise.open_sensor', return_value=mock_coro()) + @patch('haffmpeg.sensor.SensorNoise.open_sensor', + return_value=mock_coro()) def test_setup_component_start(self, mock_start): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): @@ -48,7 +49,7 @@ def test_setup_component_start(self, mock_start): entity = self.hass.states.get('binary_sensor.ffmpeg_noise') assert entity.state == 'unavailable' - @patch('haffmpeg.SensorNoise') + @patch('haffmpeg.sensor.SensorNoise') def test_setup_component_start_callback(self, mock_ffmpeg): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): @@ -95,7 +96,8 @@ def test_setup_component(self): assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_motion') is not None - @patch('haffmpeg.SensorMotion.open_sensor', return_value=mock_coro()) + @patch('haffmpeg.sensor.SensorMotion.open_sensor', + return_value=mock_coro()) def test_setup_component_start(self, mock_start): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): @@ -110,7 +112,7 @@ def test_setup_component_start(self, mock_start): entity = self.hass.states.get('binary_sensor.ffmpeg_motion') assert entity.state == 'unavailable' - @patch('haffmpeg.SensorMotion') + @patch('haffmpeg.sensor.SensorMotion') def test_setup_component_start_callback(self, mock_ffmpeg): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): From fa9a6f072eb1c87c5d329b8ff7c1c11acb388f9d Mon Sep 17 00:00:00 2001 From: zewelor Date: Wed, 27 Mar 2019 08:02:30 +0100 Subject: [PATCH 185/605] Add myself as codeowner for yeelight component (#22438) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 717da8b219ef0b..d95f2f8f9463ed 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -278,7 +278,7 @@ homeassistant/components/xiaomi_tv/media_player.py @fattdev # Y homeassistant/components/yamaha_musiccast/* @jalmeroth -homeassistant/components/yeelight/light.py @rytilahti +homeassistant/components/yeelight/* @rytilahti @zewelor homeassistant/components/yeelightsunflower/light.py @lindsaymarkward homeassistant/components/yi/camera.py @bachya From 6540114ec51103fe20858139c06cec8e9cc69ef1 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 27 Mar 2019 07:17:10 -0400 Subject: [PATCH 186/605] Update ZHA component CODEOWNERS (#22452) --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index d95f2f8f9463ed..e9d7a652a66600 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -284,6 +284,7 @@ homeassistant/components/yi/camera.py @bachya # Z homeassistant/components/zeroconf/* @robbiet480 +homeassistant/components/zha/* @dmulcahey @adminiuga homeassistant/components/zoneminder/* @rohankapoorcom # Other code From 4de2efd07fe49a09dbdc125d4acfba92e298ae33 Mon Sep 17 00:00:00 2001 From: zewelor Date: Wed, 27 Mar 2019 13:39:55 +0100 Subject: [PATCH 187/605] Add support for yeelight ceiling ambilight (#22346) --- homeassistant/components/yeelight/__init__.py | 28 +++- homeassistant/components/yeelight/light.py | 120 ++++++++++++++---- 2 files changed, 119 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 7318b088ab4da3..4171005d9fcc89 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -91,6 +91,7 @@ UPDATE_REQUEST_PROPERTIES = [ "power", + "main_power", "bright", "ct", "rgb", @@ -98,6 +99,13 @@ "sat", "color_mode", "bg_power", + "bg_lmode", + "bg_flowing", + "bg_ct", + "bg_bright", + "bg_hue", + "bg_sat", + "bg_rgb", "nl_br", "active_mode", ] @@ -249,22 +257,34 @@ def is_nightlight_supported(self) -> bool: """Return true / false if nightlight is supported.""" return self.bulb.get_model_specs().get('night_light', False) - def turn_on(self, duration=DEFAULT_TRANSITION): + @property + def is_ambilight_supported(self) -> bool: + """Return true / false if ambilight is supported.""" + return self.bulb.get_model_specs().get('background_light', False) + + def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn on device.""" import yeelight + if not light_type: + light_type = yeelight.enums.LightType.Main + try: - self._bulb_device.turn_on(duration=duration) + self._bulb_device.turn_on(duration=duration, light_type=light_type) except yeelight.BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return - def turn_off(self, duration=DEFAULT_TRANSITION): + def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn off device.""" import yeelight + if not light_type: + light_type = yeelight.enums.LightType.Main + try: - self._bulb_device.turn_off(duration=duration) + self._bulb_device.turn_off(duration=duration, + light_type=light_type) except yeelight.BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 22e5d9cc9cefed..cc3810c49685e2 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -110,10 +110,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): _LOGGER.debug("Adding %s", device.name) custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] - light = YeelightLight(device, custom_effects=custom_effects) - hass.data[data_key].append(light) - add_entities([light], True) + lights = [YeelightLight(device, custom_effects=custom_effects)] + + if device.is_ambilight_supported: + lights.append( + YeelightAmbientLight(device, custom_effects=custom_effects)) + + hass.data[data_key] += lights + add_entities(lights, True) def service_handler(service): """Dispatch service calls to target entities.""" @@ -243,9 +248,16 @@ def custom_effects_names(self): """Return list with custom effects names.""" return list(self.custom_effects.keys()) + @property + def light_type(self): + """Return light type.""" + import yeelight + return yeelight.enums.LightType.Main + def _get_hs_from_properties(self): - rgb = self._properties.get('rgb', None) - color_mode = self._properties.get('color_mode', None) + rgb = self._get_property('rgb') + color_mode = self._get_property('color_mode') + if not rgb or not color_mode: return None @@ -254,8 +266,9 @@ def _get_hs_from_properties(self): temp_in_k = mired_to_kelvin(self._color_temp) return color_util.color_temperature_to_hs(temp_in_k) if color_mode == 3: # hsv - hue = int(self._properties.get('hue')) - sat = int(self._properties.get('sat')) + hue = int(self._get_property('hue')) + sat = int(self._get_property('sat')) + return (hue / 360 * 65536, sat / 100 * 255) rgb = int(rgb) @@ -276,11 +289,18 @@ def _properties(self) -> dict: return {} return self._bulb.last_properties + def _get_property(self, prop, default=None): + return self._properties.get(prop, default) + @property def device(self): """Return yeelight device.""" return self._device + @property + def _is_nightlight_enabled(self): + return self.device.is_nightlight_enabled + # F821: https://github.com/PyCQA/pyflakes/issues/373 @property def _bulb(self) -> 'yeelight.Bulb': # noqa: F821 @@ -304,32 +324,41 @@ def update(self) -> None: """Update properties from the bulb.""" import yeelight try: - if self._bulb.bulb_type == yeelight.BulbType.Color: + bulb_type = self._bulb.bulb_type + + if bulb_type == yeelight.BulbType.Color: + self._supported_features = SUPPORT_YEELIGHT_RGB + elif self.light_type == yeelight.enums.LightType.Ambient: self._supported_features = SUPPORT_YEELIGHT_RGB - elif self._bulb.bulb_type == yeelight.BulbType.WhiteTemp: - if self._device.is_nightlight_enabled: + elif bulb_type in (yeelight.BulbType.WhiteTemp, + yeelight.BulbType.WhiteTempMood): + if self._is_nightlight_enabled: self._supported_features = SUPPORT_YEELIGHT else: self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP - if self._min_mireds is None: + if self.min_mireds is None: model_specs = self._bulb.get_model_specs() self._min_mireds = \ kelvin_to_mired(model_specs['color_temp']['max']) self._max_mireds = \ kelvin_to_mired(model_specs['color_temp']['min']) - self._is_on = self._properties.get('power') == 'on' + if bulb_type == yeelight.BulbType.WhiteTempMood: + self._is_on = self._get_property('main_power') == 'on' + else: + self._is_on = self._get_property('power') == 'on' - if self._device.is_nightlight_enabled: - bright = self._properties.get('nl_br', None) + if self._is_nightlight_enabled: + bright = self._get_property('nl_br', None) else: - bright = self._properties.get('bright', None) + bright = self._get_property('bright', None) if bright: self._brightness = round(255 * (int(bright) / 100)) - temp_in_k = self._properties.get('ct', None) + temp_in_k = self._get_property('ct') + if temp_in_k: self._color_temp = kelvin_to_mired(int(temp_in_k)) @@ -347,14 +376,16 @@ def set_brightness(self, brightness, duration) -> None: if brightness: _LOGGER.debug("Setting brightness: %s", brightness) self._bulb.set_brightness(brightness / 255 * 100, - duration=duration) + duration=duration, + light_type=self.light_type) @_cmd def set_rgb(self, rgb, duration) -> None: """Set bulb's color.""" if rgb and self.supported_features & SUPPORT_COLOR: _LOGGER.debug("Setting RGB: %s", rgb) - self._bulb.set_rgb(rgb[0], rgb[1], rgb[2], duration=duration) + self._bulb.set_rgb(rgb[0], rgb[1], rgb[2], duration=duration, + light_type=self.light_type) @_cmd def set_colortemp(self, colortemp, duration) -> None: @@ -363,7 +394,8 @@ def set_colortemp(self, colortemp, duration) -> None: temp_in_k = mired_to_kelvin(colortemp) _LOGGER.debug("Setting color temp: %s K", temp_in_k) - self._bulb.set_color_temp(temp_in_k, duration=duration) + self._bulb.set_color_temp(temp_in_k, duration=duration, + light_type=self.light_type) @_cmd def set_default(self) -> None: @@ -401,7 +433,7 @@ def set_flash(self, flash) -> None: flow = Flow(count=count, transitions=transitions) try: - self._bulb.start_flow(flow) + self._bulb.start_flow(flow, light_type=self.light_type) except BulbException as ex: _LOGGER.error("Unable to set flash: %s", ex) @@ -415,7 +447,7 @@ def set_effect(self, effect) -> None: police2, christmas, rgb, randomloop, lsd, slowdown) if effect == EFFECT_STOP: - self._bulb.stop_flow() + self._bulb.stop_flow(light_type=self.light_type) return effects_map = { @@ -447,7 +479,7 @@ def set_effect(self, effect) -> None: flow = Flow(count=2, transitions=pulse(0, 172, 237)) try: - self._bulb.start_flow(flow) + self._bulb.start_flow(flow, light_type=self.light_type) except BulbException as ex: _LOGGER.error("Unable to set effect: %s", ex) @@ -465,7 +497,7 @@ def turn_on(self, **kwargs) -> None: if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - self.device.turn_on(duration=duration) + self.device.turn_on(duration=duration, light_type=self.light_type) if self.config[CONF_MODE_MUSIC] and not self._bulb.music_mode: try: @@ -502,7 +534,7 @@ def turn_off(self, **kwargs) -> None: if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - self.device.turn_off(duration=duration) + self.device.turn_off(duration=duration, light_type=self.light_type) self.device.update() def set_mode(self, mode: str): @@ -525,7 +557,45 @@ def start_flow(self, transitions, count=0, action=ACTION_RECOVER): action=yeelight.Flow.actions[action], transitions=transitions) - self._bulb.start_flow(flow) + self._bulb.start_flow(flow, light_type=self.light_type) self.device.update() except yeelight.BulbException as ex: _LOGGER.error("Unable to set effect: %s", ex) + + +class YeelightAmbientLight(YeelightLight): + """Representation of a Yeelight ambient light.""" + + PROPERTIES_MAPPING = { + "color_mode": "bg_lmode", + "main_power": "bg_power", + } + + def __init__(self, *args, **kwargs): + """Initialize the Yeelight Ambient light.""" + super().__init__(*args, **kwargs) + self._min_mireds = kelvin_to_mired(6500) + self._max_mireds = kelvin_to_mired(1700) + + @property + def name(self) -> str: + """Return the name of the device if any.""" + return "{} ambilight".format(self.device.name) + + @property + def light_type(self): + """Return light type.""" + import yeelight + return yeelight.enums.LightType.Ambient + + @property + def _is_nightlight_enabled(self): + return False + + def _get_property(self, prop, default=None): + bg_prop = self.PROPERTIES_MAPPING.get(prop) + + if not bg_prop: + bg_prop = "bg_" + prop + + return self._properties.get(bg_prop, default) From 646c4a71375c8af8efedd9dc6217c5064fba3083 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Wed, 27 Mar 2019 22:06:20 +0800 Subject: [PATCH 188/605] Bootstrap to start registry loading early (#22321) * Registries store directly in data on loading. * Loading registries concurent with stage 1. * Removed comments --- homeassistant/bootstrap.py | 7 ++++++ homeassistant/helpers/area_registry.py | 25 +++++++++++++------ homeassistant/helpers/device_registry.py | 31 +++++++++++++++--------- homeassistant/helpers/entity_registry.py | 30 +++++++++++++++-------- tests/common.py | 18 +++----------- 5 files changed, 67 insertions(+), 44 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index a3b1d6d305e368..435ec317985315 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -1,4 +1,5 @@ """Provide methods to bootstrap a Home Assistant instance.""" +import asyncio import logging import logging.handlers import os @@ -157,6 +158,12 @@ async def async_from_config_dict(config: Dict[str, Any], await hass.async_block_till_done() + # Kick off loading the registries. They don't need to be awaited. + asyncio.gather( + hass.helpers.device_registry.async_get_registry(), + hass.helpers.entity_registry.async_get_registry(), + hass.helpers.area_registry.async_get_registry()) + # stage 1 for component in components: if component in FIRST_INIT_COMPONENT: diff --git a/homeassistant/helpers/area_registry.py b/homeassistant/helpers/area_registry.py index 644d14cf869764..adf5410516de15 100644 --- a/homeassistant/helpers/area_registry.py +++ b/homeassistant/helpers/area_registry.py @@ -1,6 +1,7 @@ """Provide a way to connect devices to one physical location.""" import logging import uuid +from asyncio import Event from collections import OrderedDict from typing import MutableMapping # noqa: F401 from typing import Iterable, Optional, cast @@ -9,6 +10,7 @@ from homeassistant.core import callback from homeassistant.loader import bind_hass + from .typing import HomeAssistantType _LOGGER = logging.getLogger(__name__) @@ -133,14 +135,21 @@ def _data_to_save(self) -> dict: @bind_hass async def async_get_registry(hass: HomeAssistantType) -> AreaRegistry: """Return area registry instance.""" - task = hass.data.get(DATA_REGISTRY) + reg_or_evt = hass.data.get(DATA_REGISTRY) + + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() + + reg = AreaRegistry(hass) + await reg.async_load() - if task is None: - async def _load_reg() -> AreaRegistry: - registry = AreaRegistry(hass) - await registry.async_load() - return registry + hass.data[DATA_REGISTRY] = reg + evt.set() + return reg - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + if isinstance(reg_or_evt, Event): + evt = reg_or_evt + await evt.wait() + return cast(AreaRegistry, hass.data.get(DATA_REGISTRY)) - return cast(AreaRegistry, await task) + return cast(AreaRegistry, reg_or_evt) diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 1ea6c400208c41..25c9933fd1116e 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -1,15 +1,17 @@ """Provide a way to connect entities belonging to one device.""" import logging import uuid -from typing import List, Optional - +from asyncio import Event from collections import OrderedDict +from typing import List, Optional, cast import attr from homeassistant.core import callback from homeassistant.loader import bind_hass +from .typing import HomeAssistantType + _LOGGER = logging.getLogger(__name__) _UNDEF = object() @@ -273,19 +275,26 @@ def async_clear_area_id(self, area_id: str) -> None: @bind_hass -async def async_get_registry(hass) -> DeviceRegistry: +async def async_get_registry(hass: HomeAssistantType) -> DeviceRegistry: """Return device registry instance.""" - task = hass.data.get(DATA_REGISTRY) + reg_or_evt = hass.data.get(DATA_REGISTRY) + + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() + + reg = DeviceRegistry(hass) + await reg.async_load() - if task is None: - async def _load_reg(): - registry = DeviceRegistry(hass) - await registry.async_load() - return registry + hass.data[DATA_REGISTRY] = reg + evt.set() + return reg - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + if isinstance(reg_or_evt, Event): + evt = reg_or_evt + await evt.wait() + return cast(DeviceRegistry, hass.data.get(DATA_REGISTRY)) - return await task + return cast(DeviceRegistry, reg_or_evt) @callback diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index c0a0dfaa7d92b7..be50d11d17d220 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -7,10 +7,11 @@ registered. Registering a new entity while a timer is in progress resets the timer. """ +from asyncio import Event from collections import OrderedDict from itertools import chain import logging -from typing import Optional, List +from typing import List, Optional, cast import weakref import attr @@ -20,6 +21,8 @@ from homeassistant.util import ensure_unique_string, slugify from homeassistant.util.yaml import load_yaml +from .typing import HomeAssistantType + PATH_REGISTRY = 'entity_registry.yaml' DATA_REGISTRY = 'entity_registry' SAVE_DELAY = 10 @@ -277,19 +280,26 @@ def async_clear_config_entry(self, config_entry): @bind_hass -async def async_get_registry(hass) -> EntityRegistry: +async def async_get_registry(hass: HomeAssistantType) -> EntityRegistry: """Return entity registry instance.""" - task = hass.data.get(DATA_REGISTRY) + reg_or_evt = hass.data.get(DATA_REGISTRY) + + if not reg_or_evt: + evt = hass.data[DATA_REGISTRY] = Event() + + reg = EntityRegistry(hass) + await reg.async_load() - if task is None: - async def _load_reg(): - registry = EntityRegistry(hass) - await registry.async_load() - return registry + hass.data[DATA_REGISTRY] = reg + evt.set() + return reg - task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) + if isinstance(reg_or_evt, Event): + evt = reg_or_evt + await evt.wait() + return cast(EntityRegistry, hass.data.get(DATA_REGISTRY)) - return await task + return cast(EntityRegistry, reg_or_evt) @callback diff --git a/tests/common.py b/tests/common.py index 8681db1b4f3c00..9fe5375ad7cc25 100644 --- a/tests/common.py +++ b/tests/common.py @@ -327,11 +327,7 @@ def mock_registry(hass, mock_entries=None): registry = entity_registry.EntityRegistry(hass) registry.entities = mock_entries or OrderedDict() - async def _get_reg(): - return registry - - hass.data[entity_registry.DATA_REGISTRY] = \ - hass.loop.create_task(_get_reg()) + hass.data[entity_registry.DATA_REGISTRY] = registry return registry @@ -340,11 +336,7 @@ def mock_area_registry(hass, mock_entries=None): registry = area_registry.AreaRegistry(hass) registry.areas = mock_entries or OrderedDict() - async def _get_reg(): - return registry - - hass.data[area_registry.DATA_REGISTRY] = \ - hass.loop.create_task(_get_reg()) + hass.data[area_registry.DATA_REGISTRY] = registry return registry @@ -353,11 +345,7 @@ def mock_device_registry(hass, mock_entries=None): registry = device_registry.DeviceRegistry(hass) registry.devices = mock_entries or OrderedDict() - async def _get_reg(): - return registry - - hass.data[device_registry.DATA_REGISTRY] = \ - hass.loop.create_task(_get_reg()) + hass.data[device_registry.DATA_REGISTRY] = registry return registry From 52437f6246b8fe592807f45a3933d796fe9ebfbc Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 27 Mar 2019 18:25:01 +0100 Subject: [PATCH 189/605] Axis devices support device registry (#22367) * Add support for device registry * Fix test --- homeassistant/components/axis/__init__.py | 2 ++ homeassistant/components/axis/binary_sensor.py | 13 +++++++++++++ homeassistant/components/axis/camera.py | 12 ++++++++++++ homeassistant/components/axis/device.py | 17 ++++++++++++++++- tests/components/axis/test_init.py | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/axis/__init__.py b/homeassistant/components/axis/__init__.py index 324c2cf369e8a5..53087f2682c862 100644 --- a/homeassistant/components/axis/__init__.py +++ b/homeassistant/components/axis/__init__.py @@ -52,6 +52,8 @@ async def async_setup_entry(hass, config_entry): hass.data[DOMAIN][device.serial] = device + await device.async_update_device_registry() + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.shutdown) return True diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index ec4c27ea34357b..3a9583f64193c9 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -81,7 +81,20 @@ def device_class(self): """Return the class of the event.""" return self.event.event_class + @property + def unique_id(self): + """Return a unique identifier for this device.""" + return '{}-{}-{}'.format( + self.device.serial, self.event.topic, self.event.id) + @property def should_poll(self): """No polling needed.""" return False + + @property + def device_info(self): + """Return a device description for device registry.""" + return { + 'identifiers': {(AXIS_DOMAIN, self.device.serial)} + } diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index 60dab841048d2d..45801257d00463 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -57,3 +57,15 @@ def _new_ip(self, host): """Set new IP for video stream.""" self._mjpeg_url = AXIS_VIDEO.format(host, self.port) self._still_image_url = AXIS_IMAGE.format(host, self.port) + + @property + def unique_id(self): + """Return a unique identifier for this device.""" + return '{}-camera'.format(self.device.serial) + + @property + def device_info(self): + """Return a device description for device registry.""" + return { + 'identifiers': {(AXIS_DOMAIN, self.device.serial)} + } diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index 02591e348a5dbf..0d7d9348870be3 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -8,9 +8,10 @@ CONF_USERNAME) from homeassistant.core import callback from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_send -from .const import CONF_CAMERA, CONF_EVENTS, CONF_MODEL, LOGGER +from .const import CONF_CAMERA, CONF_EVENTS, CONF_MODEL, DOMAIN, LOGGER from .errors import AuthenticationRequired, CannotConnect @@ -49,6 +50,20 @@ def serial(self): """Return the mac of this device.""" return self.config_entry.data[CONF_MAC] + async def async_update_device_registry(self): + """Update device registry.""" + device_registry = await \ + self.hass.helpers.device_registry.async_get_registry() + device_registry.async_get_or_create( + config_entry_id=self.config_entry.entry_id, + connections={(CONNECTION_NETWORK_MAC, self.serial)}, + identifiers={(DOMAIN, self.serial)}, + manufacturer='Axis Communications AB', + model="{} {}".format(self.model, self.product_type), + name=self.name, + sw_version=self.fw_version + ) + async def async_setup(self): """Set up the device.""" from axis.vapix import VAPIX_FW_VERSION, VAPIX_PROD_TYPE diff --git a/tests/components/axis/test_init.py b/tests/components/axis/test_init.py index 0586ffd96f6640..c1c4c06f6acd5a 100644 --- a/tests/components/axis/test_init.py +++ b/tests/components/axis/test_init.py @@ -53,6 +53,7 @@ async def test_setup_entry(hass): mock_device = Mock() mock_device.async_setup.return_value = mock_coro(True) + mock_device.async_update_device_registry.return_value = mock_coro(True) mock_device.serial.return_value = '1' with patch.object(axis, 'AxisNetworkDevice') as mock_device_class, \ From 29ad3961e581d3591ce0963a7fa01672abadedf7 Mon Sep 17 00:00:00 2001 From: Leonardo Merza Date: Wed, 27 Mar 2019 13:40:39 -0400 Subject: [PATCH 190/605] Use voluptuous error string for websocket validation error (#21883) * use voluptuous error string to websocket validation error * added exception logging to websocket error * add detailed message to websocket validation error * add error message to websocket validation error * Add humanize error for websocket invalid vol error * Add humanize error for websocket invalid vol error * Add humanize error for websocket invalid vol error --- .../components/websocket_api/connection.py | 4 ++-- tests/components/websocket_api/test_init.py | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/websocket_api/connection.py b/homeassistant/components/websocket_api/connection.py index d65ba4c54d848c..c09e8c4c6e2965 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -93,11 +93,11 @@ def async_handle_exception(self, msg, err): err_message = 'Unauthorized' elif isinstance(err, vol.Invalid): code = const.ERR_INVALID_FORMAT - err_message = 'Invalid format' + err_message = vol.humanize.humanize_error(msg, err) else: - self.logger.exception('Error handling message: %s', msg) code = const.ERR_UNKNOWN_ERROR err_message = 'Unknown error' + self.logger.exception('Error handling message: %s', err_message) self.send_message( messages.error_message(msg['id'], code, err_message)) diff --git a/tests/components/websocket_api/test_init.py b/tests/components/websocket_api/test_init.py index 272ac3870edce1..08ea655fdf0d3f 100644 --- a/tests/components/websocket_api/test_init.py +++ b/tests/components/websocket_api/test_init.py @@ -4,6 +4,7 @@ from aiohttp import WSMsgType import pytest +import voluptuous as vol from homeassistant.components.websocket_api import const, messages @@ -90,3 +91,26 @@ async def test_handler_failing(hass, websocket_client): assert msg['type'] == const.TYPE_RESULT assert not msg['success'] assert msg['error']['code'] == const.ERR_UNKNOWN_ERROR + + +async def test_invalid_vol(hass, websocket_client): + """Test a command that raises invalid vol error.""" + hass.components.websocket_api.async_register_command( + 'bla', Mock(side_effect=TypeError), + messages.BASE_COMMAND_MESSAGE_SCHEMA.extend({ + 'type': 'bla', + vol.Required('test_config'): str + })) + + await websocket_client.send_json({ + 'id': 5, + 'type': 'bla', + 'test_config': 5 + }) + + msg = await websocket_client.receive_json() + assert msg['id'] == 5 + assert msg['type'] == const.TYPE_RESULT + assert not msg['success'] + assert msg['error']['code'] == const.ERR_INVALID_FORMAT + assert 'expected str for dictionary value' in msg['error']['message'] From c3f090af1720bef43c6ba1e9fdeb3150f39a6de5 Mon Sep 17 00:00:00 2001 From: Ryan Claussen Date: Wed, 27 Mar 2019 15:11:25 -0500 Subject: [PATCH 191/605] Add hourly forecasts to Dark Sky (#21820) --- homeassistant/components/darksky/sensor.py | 34 +++++++++++++++++----- tests/components/darksky/test_sensor.py | 6 +++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index c68bb2cd3a383b..540568b5785dfc 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -27,6 +27,7 @@ ATTRIBUTION = "Powered by Dark Sky" CONF_FORECAST = 'forecast' +CONF_HOURLY_FORECAST = 'hourly_forecast' CONF_LANGUAGE = 'language' CONF_UNITS = 'units' @@ -193,6 +194,8 @@ vol.All(cv.time_period, cv.positive_timedelta), vol.Optional(CONF_FORECAST): vol.All(cv.ensure_list, [vol.Range(min=0, max=7)]), + vol.Optional(CONF_HOURLY_FORECAST): + vol.All(cv.ensure_list, [vol.Range(min=0, max=48)]), }), cv.deprecated( CONF_UPDATE_INTERVAL, @@ -230,6 +233,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = config.get(CONF_NAME) forecast = config.get(CONF_FORECAST) + forecast_hour = config.get(CONF_HOURLY_FORECAST) sensors = [] for variable in config[CONF_MONITORED_CONDITIONS]: if variable in DEPRECATED_SENSOR_TYPES: @@ -240,7 +244,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None): if forecast is not None and 'daily' in SENSOR_TYPES[variable][7]: for forecast_day in forecast: sensors.append(DarkSkySensor( - forecast_data, variable, name, forecast_day)) + forecast_data, variable, name, forecast_day=forecast_day)) + if forecast_hour is not None and 'hourly' in SENSOR_TYPES[variable][7]: + for forecast_h in forecast_hour: + sensors.append(DarkSkySensor( + forecast_data, variable, name, forecast_hour=forecast_h)) add_entities(sensors, True) @@ -248,13 +256,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class DarkSkySensor(Entity): """Implementation of a Dark Sky sensor.""" - def __init__(self, forecast_data, sensor_type, name, forecast_day=None): + def __init__(self, forecast_data, sensor_type, name, + forecast_day=None, forecast_hour=None): """Initialize the sensor.""" self.client_name = name self._name = SENSOR_TYPES[sensor_type][0] self.forecast_data = forecast_data self.type = sensor_type self.forecast_day = forecast_day + self.forecast_hour = forecast_hour self._state = None self._icon = None self._unit_of_measurement = None @@ -262,11 +272,14 @@ def __init__(self, forecast_data, sensor_type, name, forecast_day=None): @property def name(self): """Return the name of the sensor.""" - if self.forecast_day is None: - return '{} {}'.format(self.client_name, self._name) - - return '{} {} {}'.format( - self.client_name, self._name, self.forecast_day) + if self.forecast_day is not None: + return '{} {} {}d'.format( + self.client_name, self._name, self.forecast_day) + if self.forecast_hour is not None: + return '{} {} {}h'.format( + self.client_name, self._name, self.forecast_hour) + return '{} {}'.format( + self.client_name, self._name) @property def state(self): @@ -339,6 +352,13 @@ def update(self): hourly = self.forecast_data.data_hourly self._state = getattr(hourly, 'summary', '') self._icon = getattr(hourly, 'icon', '') + elif self.forecast_hour is not None: + self.forecast_data.update_hourly() + hourly = self.forecast_data.data_hourly + if hasattr(hourly, 'data'): + self._state = self.get_state(hourly.data[self.forecast_hour]) + else: + self._state = 0 elif self.type == 'daily_summary': self.forecast_data.update_daily() daily = self.forecast_data.data_daily diff --git a/tests/components/darksky/test_sensor.py b/tests/components/darksky/test_sensor.py index 25e54fac8232c1..ffb90d8474b11f 100644 --- a/tests/components/darksky/test_sensor.py +++ b/tests/components/darksky/test_sensor.py @@ -20,6 +20,7 @@ 'platform': 'darksky', 'api_key': 'foo', 'forecast': [1, 2], + 'hourly_forecast': [1, 2], 'monitored_conditions': ['summary', 'icon', 'temperature_high'], 'scan_interval': timedelta(seconds=120), } @@ -30,6 +31,7 @@ 'platform': 'darksky', 'api_key': 'foo', 'forecast': [1, 2], + 'hourly_forecast': [1, 2], 'monitored_conditions': ['sumary', 'iocn', 'temperature_high'], 'scan_interval': timedelta(seconds=120), } @@ -40,6 +42,7 @@ 'platform': 'darksky', 'api_key': 'foo', 'forecast': [1, 2], + 'hourly_forecast': [1, 2], 'units': 'us', 'language': 'de', 'monitored_conditions': ['summary', 'icon', 'temperature_high', @@ -54,6 +57,7 @@ 'platform': 'darksky', 'api_key': 'foo', 'forecast': [1, 2], + 'hourly_forecast': [1, 2], 'language': 'yz', 'monitored_conditions': ['summary', 'icon', 'temperature_high'], 'scan_interval': timedelta(seconds=120), @@ -157,7 +161,7 @@ def test_setup(self, mock_req, mock_get_forecast): assert mock_get_forecast.called assert mock_get_forecast.call_count == 1 - assert len(self.hass.states.entity_ids()) == 8 + assert len(self.hass.states.entity_ids()) == 12 state = self.hass.states.get('sensor.dark_sky_summary') assert state is not None From 71b800457b130ee6f3bad7cf7198dd7107e72025 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 27 Mar 2019 21:37:21 +0100 Subject: [PATCH 192/605] Add switches to control Daikin Airbase zones (#22417) * initial AirBase zone support * fix zone name * version bump pydaikin * don't use get() --- homeassistant/components/daikin/__init__.py | 6 +- homeassistant/components/daikin/switch.py | 77 +++++++++++++++++++++ requirements_all.txt | 2 +- 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/daikin/switch.py diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index c757185a5cffe4..a09991e3b7225f 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,16 +17,16 @@ from . import config_flow # noqa pylint_disable=unused-import from .const import KEY_HOST -REQUIREMENTS = ['pydaikin==1.2.0'] +REQUIREMENTS = ['pydaikin==1.3.1'] _LOGGER = logging.getLogger(__name__) DOMAIN = 'daikin' - +PARALLEL_UPDATES = 0 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) -COMPONENT_TYPES = ['climate', 'sensor'] +COMPONENT_TYPES = ['climate', 'sensor', 'switch'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ diff --git a/homeassistant/components/daikin/switch.py b/homeassistant/components/daikin/switch.py new file mode 100644 index 00000000000000..29159c60fe9e32 --- /dev/null +++ b/homeassistant/components/daikin/switch.py @@ -0,0 +1,77 @@ +"""Support for Daikin AirBase zones.""" +import logging + +from homeassistant.helpers.entity import ToggleEntity + +from . import DOMAIN as DAIKIN_DOMAIN + +_LOGGER = logging.getLogger(__name__) + +ZONE_ICON = 'mdi:home-circle' + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Old way of setting up the platform. + + Can only be called when a user accidentally mentions the platform in their + config. But even in that case it would have been ignored. + """ + pass + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up Daikin climate based on config_entry.""" + daikin_api = hass.data[DAIKIN_DOMAIN][entry.entry_id] + zones = daikin_api.device.zones + if zones: + async_add_entities([ + DaikinZoneSwitch(daikin_api, zone_id) + for zone_id in range(len(zones)) + ]) + + +class DaikinZoneSwitch(ToggleEntity): + """Representation of a zone.""" + + def __init__(self, daikin_api, zone_id): + """Initialize the zone.""" + self._api = daikin_api + self._zone_id = zone_id + + @property + def unique_id(self): + """Return a unique ID.""" + return "{}-zone{}".format(self._api.mac, self._zone_id) + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return ZONE_ICON + + @property + def name(self): + """Return the name of the sensor.""" + return "{} {}".format(self._api.name, + self._api.device.zones[self._zone_id][0]) + + @property + def is_on(self): + """Return the state of the sensor.""" + return self._api.device.zones[self._zone_id][1] == '1' + + @property + def device_info(self): + """Return a device description for device registry.""" + return self._api.device_info + + async def async_update(self): + """Retrieve latest state.""" + await self._api.async_update() + + async def async_turn_on(self, **kwargs): + """Turn the zone on.""" + await self._api.device.set_zone(self._zone_id, '1') + + async def async_turn_off(self, **kwargs): + """Turn the zone off.""" + await self._api.device.set_zone(self._zone_id, '0') diff --git a/requirements_all.txt b/requirements_all.txt index 4b4b2570d50c64..21d2e0365d7f03 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -991,7 +991,7 @@ pycoolmasternet==0.0.4 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==1.2.0 +pydaikin==1.3.1 # homeassistant.components.danfoss_air pydanfossair==0.0.7 From 2b48ecd5c5a3c1633dfed067550961fb1af01b8f Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Wed, 27 Mar 2019 16:47:03 -0400 Subject: [PATCH 193/605] better algorithm for computing unique_id (#22389) --- homeassistant/components/konnected/switch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index dfb135e19f618b..7384d62900eec9 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -7,7 +7,7 @@ from . import ( CONF_ACTIVATION, CONF_MOMENTARY, CONF_PAUSE, CONF_REPEAT, - DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, STATE_HIGH, STATE_LOW) + DOMAIN as KONNECTED_DOMAIN, STATE_HIGH, STATE_LOW) _LOGGER = logging.getLogger(__name__) @@ -41,7 +41,8 @@ def __init__(self, device_id, pin_num, data): self._pause = self._data.get(CONF_PAUSE) self._repeat = self._data.get(CONF_REPEAT) self._state = self._boolean_state(self._data.get(ATTR_STATE)) - self._unique_id = '{}-{}'.format(device_id, PIN_TO_ZONE[pin_num]) + self._unique_id = '{}-{}'.format(device_id, hash(frozenset( + {self._pin_num, self._momentary, self._pause, self._repeat}))) self._name = self._data.get(CONF_NAME) @property From 24c7c2aa6e5c1e87f983167450084bb180559281 Mon Sep 17 00:00:00 2001 From: Gido Date: Wed, 27 Mar 2019 22:08:52 +0100 Subject: [PATCH 194/605] Solaredge new sensors (#21047) * Remove unused hass parameter for SolarEdgeData * Add factory to create different types of sensors Rename SolarEdgeSensor to SolarEdgeOverviewSensor Rename SolarEdgeData to SolarEdgeOverviewDataService Remove unused hass parameter in SolarEdgeOverviewDataService * Add SolarEdgeDetailsDataService to retrieve details data Add SolarEdgeDetailsSensor to report details data Add abstract class SolarEdgeSensor Add details sensor types * Combine multiple details sensor into one sensor with attributes * Fix pylint and flake8 errors * Resolve conflict with solaredge component update * Add SolarEdgeInventoryDataService to retrieve inventory information Add SolarEdgeInventorySensor to view inventory information Add inverters to monitored_conditions * Fix pylint and flake8 errors * Add additional monitored variables for solaredge * Add new sensors to solaredge component * Add SolarEdgePowerFlowDataService Add SolarEdgePowerFlowSensor Add new monitored_conditions for power consumption and grid, load and solar power production/consumption * Set entity_id for each sensor based on platform and sensor type * Fix flake8 and pylint errors * Add check for connections in return data * Fix pylint and flake8 errors * Renamed state_attributes to device_state_attributes Moved request import to top * Remove explicit definition of entity_id * Fix pylint and flake8 errors * Add check for None before adding sensor * Update SolarEdgeSensorFactory with initial dict which maps sensor_key to entity class and data service * Update attribute values to snakecase Added stingcase as requirement * Update requirements_all.txt to include stringcase for solaredge * Update some initial values for data and unit_of_measurement Update sensor factory --- homeassistant/components/solaredge/sensor.py | 321 +++++++++++++++++-- requirements_all.txt | 1 + 2 files changed, 293 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/solaredge/sensor.py b/homeassistant/components/solaredge/sensor.py index a0d76c564c115d..d56ccc53b68672 100644 --- a/homeassistant/components/solaredge/sensor.py +++ b/homeassistant/components/solaredge/sensor.py @@ -10,6 +10,7 @@ import voluptuous as vol +from requests.exceptions import HTTPError, ConnectTimeout from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_API_KEY, CONF_MONITORED_CONDITIONS, CONF_NAME, POWER_WATT, @@ -18,15 +19,19 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['solaredge==0.0.2'] +REQUIREMENTS = ['solaredge==0.0.2', 'stringcase==1.2.0'] # Config for solaredge monitoring api requests. CONF_SITE_ID = "site_id" -UPDATE_DELAY = timedelta(minutes=10) +OVERVIEW_UPDATE_DELAY = timedelta(minutes=10) +DETAILS_UPDATE_DELAY = timedelta(hours=12) +INVENTORY_UPDATE_DELAY = timedelta(hours=12) +POWER_FLOW_UPDATE_DELAY = timedelta(minutes=10) + SCAN_INTERVAL = timedelta(minutes=10) -# Supported sensor types: +# Supported overview sensor types: # Key: ['json_key', 'name', unit, icon] SENSOR_TYPES = { 'lifetime_energy': ['lifeTimeData', "Lifetime energy", @@ -37,8 +42,18 @@ ENERGY_WATT_HOUR, 'mdi:solar-power'], 'energy_today': ['lastDayData', "Energy today", ENERGY_WATT_HOUR, 'mdi:solar-power'], - 'current_power': ['currentPower', "Current Power", - POWER_WATT, 'mdi:solar-power'] + 'current_power': ['currentPower', "Current Power", POWER_WATT, + 'mdi:solar-power'], + 'site_details': [None, 'Site details', None, None], + 'meters': ['meters', 'Meters', None, None], + 'sensors': ['sensors', 'Sensors', None, None], + 'gateways': ['gateways', 'Gateways', None, None], + 'batteries': ['batteries', 'Batteries', None, None], + 'inverters': ['inverters', 'Inverters', None, None], + 'power_consumption': ['LOAD', 'Power Consumption', None, 'mdi:flash'], + 'solar_power': ['PV', 'Solar Power', None, 'mdi:solar-power'], + 'grid_power': ['GRID', 'Grid Power', None, 'mdi:power-plug'], + 'storage_power': ['STORAGE', 'Storage Power', None, 'mdi:car-battery'] } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -55,7 +70,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Create the SolarEdge Monitoring API sensor.""" import solaredge - from requests.exceptions import HTTPError, ConnectTimeout api_key = config[CONF_API_KEY] site_id = config[CONF_SITE_ID] @@ -79,30 +93,66 @@ def setup_platform(hass, config, add_entities, discovery_info=None): _LOGGER.error("Could not retrieve details from SolarEdge API") return - # Create solaredge data service which will retrieve and update the data. - data = SolarEdgeData(hass, api, site_id) + # Create sensor factory that will create sensors based on sensor_key. + sensor_factory = SolarEdgeSensorFactory(platform_name, site_id, api) # Create a new sensor for each sensor type. entities = [] for sensor_key in config[CONF_MONITORED_CONDITIONS]: - sensor = SolarEdgeSensor(platform_name, sensor_key, data) - entities.append(sensor) + sensor = sensor_factory.create_sensor(sensor_key) + if sensor is not None: + entities.append(sensor) add_entities(entities, True) +class SolarEdgeSensorFactory: + """Factory which creates sensors based on the sensor_key.""" + + def __init__(self, platform_name, site_id, api): + """Initialize the factory.""" + self.platform_name = platform_name + + details = SolarEdgeDetailsDataService(api, site_id) + overview = SolarEdgeOverviewDataService(api, site_id) + inventory = SolarEdgeInventoryDataService(api, site_id) + flow = SolarEdgePowerFlowDataService(api, site_id) + + self.services = { + 'site_details': (SolarEdgeDetailsSensor, details) + } + + for key in ['lifetime_energy', 'energy_this_year', 'energy_this_month', + 'energy_today', 'current_power']: + self.services[key] = (SolarEdgeOverviewSensor, overview) + + for key in ['meters', 'sensors', 'gateways', 'batteries', 'inverters']: + self.services[key] = (SolarEdgeInventorySensor, inventory) + + for key in ['power_consumption', 'solar_power', 'grid_power', + 'storage_power']: + self.services[key] = (SolarEdgePowerFlowSensor, flow) + + def create_sensor(self, sensor_key): + """Create and return a sensor based on the sensor_key.""" + sensor_class, service = self.services[sensor_key] + + return sensor_class(self.platform_name, sensor_key, service) + + class SolarEdgeSensor(Entity): - """Representation of an SolarEdge Monitoring API sensor.""" + """Abstract class for a solaredge sensor.""" - def __init__(self, platform_name, sensor_key, data): + def __init__(self, platform_name, sensor_key, data_service): """Initialize the sensor.""" self.platform_name = platform_name self.sensor_key = sensor_key - self.data = data + self.data_service = data_service + self._state = None - self._json_key = SENSOR_TYPES[self.sensor_key][0] self._unit_of_measurement = SENSOR_TYPES[self.sensor_key][2] + self._icon = SENSOR_TYPES[self.sensor_key][3] @property def name(self): @@ -118,34 +168,115 @@ def unit_of_measurement(self): @property def icon(self): """Return the sensor icon.""" - return SENSOR_TYPES[self.sensor_key][3] + return self._icon @property def state(self): """Return the state of the sensor.""" return self._state + +class SolarEdgeOverviewSensor(SolarEdgeSensor): + """Representation of an SolarEdge Monitoring API overview sensor.""" + + def __init__(self, platform_name, sensor_key, data_service): + """Initialize the overview sensor.""" + super().__init__(platform_name, sensor_key, data_service) + + self._json_key = SENSOR_TYPES[self.sensor_key][0] + def update(self): """Get the latest data from the sensor and update the state.""" - self.data.update() - self._state = self.data.data[self._json_key] + self.data_service.update() + self._state = self.data_service.data[self._json_key] + + +class SolarEdgeDetailsSensor(SolarEdgeSensor): + """Representation of an SolarEdge Monitoring API details sensor.""" + + def __init__(self, platform_name, sensor_key, data_service): + """Initialize the details sensor.""" + super().__init__(platform_name, sensor_key, data_service) + + self._attributes = {} + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return self._attributes + + def update(self): + """Get the latest details and update state and attributes.""" + self.data_service.update() + self._state = self.data_service.data + self._attributes = self.data_service.attributes -class SolarEdgeData: +class SolarEdgeInventorySensor(SolarEdgeSensor): + """Representation of an SolarEdge Monitoring API inventory sensor.""" + + def __init__(self, platform_name, sensor_key, data_service): + """Initialize the inventory sensor.""" + super().__init__(platform_name, sensor_key, data_service) + + self._json_key = SENSOR_TYPES[self.sensor_key][0] + + self._attributes = {} + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return self._attributes + + def update(self): + """Get the latest inventory data and update state and attributes.""" + self.data_service.update() + self._state = self.data_service.data[self._json_key] + self._attributes = self.data_service.attributes[self._json_key] + + +class SolarEdgePowerFlowSensor(SolarEdgeSensor): + """Representation of an SolarEdge Monitoring API power flow sensor.""" + + def __init__(self, platform_name, sensor_key, data_service): + """Initialize the power flow sensor.""" + super().__init__(platform_name, sensor_key, data_service) + + self._json_key = SENSOR_TYPES[self.sensor_key][0] + + self._attributes = {} + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return self._attributes + + def update(self): + """Get the latest inventory data and update state and attributes.""" + self.data_service.update() + self._state = self.data_service.data.get(self._json_key) + self._attributes = self.data_service.attributes.get(self._json_key) + self._unit_of_measurement = self.data_service.unit + + +class SolarEdgeDataService: """Get and update the latest data.""" - def __init__(self, hass, api, site_id): + def __init__(self, api, site_id): """Initialize the data object.""" - self.hass = hass self.api = api - self.data = {} self.site_id = site_id - @Throttle(UPDATE_DELAY) + self.data = {} + self.attributes = {} + + +class SolarEdgeOverviewDataService(SolarEdgeDataService): + """Get and update the latest overview data.""" + + @Throttle(OVERVIEW_UPDATE_DELAY) def update(self): """Update the data from the SolarEdge Monitoring API.""" - from requests.exceptions import HTTPError, ConnectTimeout - try: data = self.api.get_overview(self.site_id) overview = data['overview'] @@ -159,9 +290,141 @@ def update(self): self.data = {} for key, value in overview.items(): - if 'energy' in value: - self.data[key] = value['energy'] - elif 'power' in value: - self.data[key] = value['power'] + if key in ['lifeTimeData', 'lastYearData', + 'lastMonthData', 'lastDayData']: + data = value['energy'] + elif key in ['currentPower']: + data = value['power'] + else: + data = value + self.data[key] = data - _LOGGER.debug("Updated SolarEdge overview data: %s", self.data) + _LOGGER.debug("Updated SolarEdge overview: %s", self.data) + + +class SolarEdgeDetailsDataService(SolarEdgeDataService): + """Get and update the latest details data.""" + + def __init__(self, api, site_id): + """Initialize the details data service.""" + super().__init__(api, site_id) + + self.data = None + + @Throttle(DETAILS_UPDATE_DELAY) + def update(self): + """Update the data from the SolarEdge Monitoring API.""" + from stringcase import snakecase + + try: + data = self.api.get_details(self.site_id) + details = data['details'] + except KeyError: + _LOGGER.error("Missing details data, skipping update") + return + except (ConnectTimeout, HTTPError): + _LOGGER.error("Could not retrieve data, skipping update") + return + + self.data = None + self.attributes = {} + + for key, value in details.items(): + key = snakecase(key) + + if key in ['primary_module']: + for module_key, module_value in value.items(): + self.attributes[snakecase(module_key)] = module_value + elif key in ['peak_power', 'type', 'name', 'last_update_time', + 'installation_date']: + self.attributes[key] = value + elif key == 'status': + self.data = value + + _LOGGER.debug("Updated SolarEdge details: %s, %s", + self.data, self.attributes) + + +class SolarEdgeInventoryDataService(SolarEdgeDataService): + """Get and update the latest inventory data.""" + + @Throttle(INVENTORY_UPDATE_DELAY) + def update(self): + """Update the data from the SolarEdge Monitoring API.""" + try: + data = self.api.get_inventory(self.site_id) + inventory = data['Inventory'] + except KeyError: + _LOGGER.error("Missing inventory data, skipping update") + return + except (ConnectTimeout, HTTPError): + _LOGGER.error("Could not retrieve data, skipping update") + return + + self.data = {} + self.attributes = {} + + for key, value in inventory.items(): + self.data[key] = len(value) + self.attributes[key] = {key: value} + + _LOGGER.debug("Updated SolarEdge inventory: %s, %s", + self.data, self.attributes) + + +class SolarEdgePowerFlowDataService(SolarEdgeDataService): + """Get and update the latest power flow data.""" + + def __init__(self, api, site_id): + """Initialize the power flow data service.""" + super().__init__(api, site_id) + + self.unit = None + + @Throttle(POWER_FLOW_UPDATE_DELAY) + def update(self): + """Update the data from the SolarEdge Monitoring API.""" + try: + data = self.api.get_current_power_flow(self.site_id) + power_flow = data['siteCurrentPowerFlow'] + except KeyError: + _LOGGER.error("Missing power flow data, skipping update") + return + except (ConnectTimeout, HTTPError): + _LOGGER.error("Could not retrieve data, skipping update") + return + + power_from = [] + power_to = [] + + if 'connections' not in power_flow: + _LOGGER.error("Missing connections in power flow data") + return + + for connection in power_flow['connections']: + power_from.append(connection['from'].lower()) + power_to.append(connection['to'].lower()) + + self.data = {} + self.attributes = {} + self.unit = power_flow['unit'] + + for key, value in power_flow.items(): + if key in ['LOAD', 'PV', 'GRID', 'STORAGE']: + self.data[key] = value['currentPower'] + self.attributes[key] = {'status': value['status']} + + if key in ['GRID']: + export = key.lower() in power_to + self.data[key] *= -1 if export else 1 + self.attributes[key]['flow'] = ('export' if export + else 'import') + + if key in ['STORAGE']: + charge = key.lower() in power_to + self.data[key] *= -1 if charge else 1 + self.attributes[key]['flow'] = ('charge' if charge + else 'discharge') + + _LOGGER.debug("Updated SolarEdge power flow: %s, %s", + self.data, self.attributes) diff --git a/requirements_all.txt b/requirements_all.txt index 21d2e0365d7f03..2e66b5b39e192b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1645,6 +1645,7 @@ statsd==3.2.1 # homeassistant.components.steam_online.sensor steamodd==4.21 +# homeassistant.components.solaredge.sensor # homeassistant.components.thermoworks_smoke.sensor # homeassistant.components.traccar.device_tracker stringcase==1.2.0 From f795d0350367b030dc9bfe35869bd12db739c307 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 27 Mar 2019 14:53:06 -0700 Subject: [PATCH 195/605] Fix aws.notify platform schema (#22374) * Fix aws component notify platform schema * Address code review comment * Do not allow load aws.notify from notify component * Revert unrelated translation update * Review comment --- homeassistant/components/aws/__init__.py | 47 +++++++-- homeassistant/components/aws/const.py | 11 +- homeassistant/components/aws/notify.py | 123 ++++++++--------------- 3 files changed, 89 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/aws/__init__.py b/homeassistant/components/aws/__init__.py index bd1f6b550909a8..a15e56e9de89cd 100644 --- a/homeassistant/components/aws/__init__.py +++ b/homeassistant/components/aws/__init__.py @@ -13,14 +13,18 @@ from . import config_flow # noqa from .const import ( CONF_ACCESS_KEY_ID, + CONF_CONTEXT, + CONF_CREDENTIAL_NAME, + CONF_CREDENTIALS, + CONF_NOTIFY, + CONF_REGION, CONF_SECRET_ACCESS_KEY, + CONF_SERVICE, DATA_CONFIG, DATA_HASS_CONFIG, DATA_SESSIONS, DOMAIN, - CONF_NOTIFY, ) -from .notify import PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA REQUIREMENTS = ["aiobotocore==0.10.2"] @@ -37,14 +41,31 @@ DEFAULT_CREDENTIAL = [{CONF_NAME: "default", CONF_PROFILE_NAME: "default"}] +SUPPORTED_SERVICES = ["lambda", "sns", "sqs"] + +NOTIFY_PLATFORM_SCHEMA = vol.Schema( + { + vol.Optional(CONF_NAME): cv.string, + vol.Required(CONF_SERVICE): vol.All( + cv.string, vol.Lower, vol.In(SUPPORTED_SERVICES) + ), + vol.Required(CONF_REGION): vol.All(cv.string, vol.Lower), + vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, + vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, + vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, + vol.Exclusive(CONF_CREDENTIAL_NAME, ATTR_CREDENTIALS): cv.string, + vol.Optional(CONF_CONTEXT): vol.Coerce(dict), + } +) + CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( { vol.Optional( - ATTR_CREDENTIALS, default=DEFAULT_CREDENTIAL + CONF_CREDENTIALS, default=DEFAULT_CREDENTIAL ): vol.All(cv.ensure_list, [AWS_CREDENTIAL_SCHEMA]), - vol.Optional(CONF_NOTIFY): vol.All( + vol.Optional(CONF_NOTIFY, default=[]): vol.All( cv.ensure_list, [NOTIFY_PLATFORM_SCHEMA] ), } @@ -98,9 +119,10 @@ async def async_setup_entry(hass, entry): if conf is None: conf = CONFIG_SCHEMA({DOMAIN: entry.data})[DOMAIN] + # validate credentials and create sessions validation = True tasks = [] - for cred in conf.get(ATTR_CREDENTIALS): + for cred in conf[ATTR_CREDENTIALS]: tasks.append(_validate_aws_credentials(hass, cred)) if tasks: results = await asyncio.gather(*tasks, return_exceptions=True) @@ -109,15 +131,22 @@ async def async_setup_entry(hass, entry): if isinstance(result, Exception): _LOGGER.error( "Validating credential [%s] failed: %s", - name, result, exc_info=result + name, + result, + exc_info=result, ) validation = False else: hass.data[DATA_SESSIONS][name] = result - # No entry support for notify component yet - for notify_config in conf.get(CONF_NOTIFY, []): - discovery.load_platform(hass, "notify", DOMAIN, notify_config, config) + # set up notify platform, no entry support for notify component yet, + # have to use discovery to load platform. + for notify_config in conf[CONF_NOTIFY]: + hass.async_create_task( + discovery.async_load_platform( + hass, "notify", DOMAIN, notify_config, config + ) + ) return validation diff --git a/homeassistant/components/aws/const.py b/homeassistant/components/aws/const.py index c8b0eed8b6bbe3..4fa88566934100 100644 --- a/homeassistant/components/aws/const.py +++ b/homeassistant/components/aws/const.py @@ -1,13 +1,16 @@ """Constant for AWS component.""" DOMAIN = "aws" -DATA_KEY = DOMAIN + DATA_CONFIG = "aws_config" DATA_HASS_CONFIG = "aws_hass_config" DATA_SESSIONS = "aws_sessions" -CONF_REGION = "region_name" CONF_ACCESS_KEY_ID = "aws_access_key_id" -CONF_SECRET_ACCESS_KEY = "aws_secret_access_key" -CONF_PROFILE_NAME = "profile_name" +CONF_CONTEXT = "context" CONF_CREDENTIAL_NAME = "credential_name" +CONF_CREDENTIALS = 'credentials' CONF_NOTIFY = "notify" +CONF_PROFILE_NAME = "profile_name" +CONF_REGION = "region_name" +CONF_SECRET_ACCESS_KEY = "aws_secret_access_key" +CONF_SERVICE = "service" diff --git a/homeassistant/components/aws/notify.py b/homeassistant/components/aws/notify.py index 020d92200b98ef..48b80b64ce2ee6 100644 --- a/homeassistant/components/aws/notify.py +++ b/homeassistant/components/aws/notify.py @@ -1,29 +1,23 @@ """AWS platform for notify component.""" import asyncio -import logging -import json import base64 +import json +import logging -import voluptuous as vol - -import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_PLATFORM, CONF_NAME, ATTR_CREDENTIALS from homeassistant.components.notify import ( ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService, - PLATFORM_SCHEMA, ) -from homeassistant.exceptions import HomeAssistantError +from homeassistant.const import CONF_PLATFORM, CONF_NAME from homeassistant.helpers.json import JSONEncoder - from .const import ( - CONF_ACCESS_KEY_ID, + CONF_CONTEXT, CONF_CREDENTIAL_NAME, CONF_PROFILE_NAME, CONF_REGION, - CONF_SECRET_ACCESS_KEY, + CONF_SERVICE, DATA_SESSIONS, ) @@ -31,69 +25,43 @@ _LOGGER = logging.getLogger(__name__) -CONF_CONTEXT = "context" -CONF_SERVICE = "service" - -SUPPORTED_SERVICES = ["lambda", "sns", "sqs"] - -def _in_avilable_region(config): - """Check if region is available.""" +async def get_available_regions(hass, service): + """Get available regions for a service.""" import aiobotocore session = aiobotocore.get_session() - available_regions = session.get_available_regions(config[CONF_SERVICE]) - if config[CONF_REGION] not in available_regions: - raise vol.Invalid( - "Region {} is not available for {} service, mustin {}".format( - config[CONF_REGION], config[CONF_SERVICE], available_regions - ) - ) - return config - - -PLATFORM_SCHEMA = vol.Schema( - vol.All( - PLATFORM_SCHEMA.extend( - { - # override notify.PLATFORM_SCHEMA.CONF_PLATFORM to Optional - # we don't need this field when we use discovery - vol.Optional(CONF_PLATFORM): cv.string, - vol.Required(CONF_SERVICE): vol.All( - cv.string, vol.Lower, vol.In(SUPPORTED_SERVICES) - ), - vol.Required(CONF_REGION): vol.All(cv.string, vol.Lower), - vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, - vol.Inclusive( - CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS - ): cv.string, - vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, - vol.Exclusive( - CONF_CREDENTIAL_NAME, ATTR_CREDENTIALS - ): cv.string, - vol.Optional(CONF_CONTEXT): vol.Coerce(dict), - }, - extra=vol.PREVENT_EXTRA, - ), - _in_avilable_region, + # get_available_regions is not a coroutine since it does not perform + # network I/O. But it still perform file I/O heavily, so put it into + # an executor thread to unblock event loop + return await hass.async_add_executor_job( + session.get_available_regions, service ) -) async def async_get_service(hass, config, discovery_info=None): """Get the AWS notification service.""" + if discovery_info is None: + _LOGGER.error('Please config aws notify platform in aws component') + return None + import aiobotocore session = None - if discovery_info is not None: - conf = discovery_info - else: - conf = config + conf = discovery_info service = conf[CONF_SERVICE] region_name = conf[CONF_REGION] + available_regions = await get_available_regions(hass, service) + if region_name not in available_regions: + _LOGGER.error( + "Region %s is not available for %s service, must in %s", + region_name, service, available_regions + ) + return None + aws_config = conf.copy() del aws_config[CONF_SERVICE] @@ -106,13 +74,14 @@ async def async_get_service(hass, config, discovery_info=None): del aws_config[CONF_CONTEXT] if not aws_config: - # no platform config, use aws component config instead + # no platform config, use the first aws component credential instead if hass.data[DATA_SESSIONS]: - session = list(hass.data[DATA_SESSIONS].values())[0] + session = next(iter(hass.data[DATA_SESSIONS].values())) else: - raise ValueError( - "No available aws session for {}".format(config[CONF_NAME]) + _LOGGER.error( + "Missing aws credential for %s", config[CONF_NAME] ) + return None if session is None: credential_name = aws_config.get(CONF_CREDENTIAL_NAME) @@ -148,7 +117,8 @@ async def async_get_service(hass, config, discovery_info=None): if service == "sqs": return AWSSQS(session, aws_config) - raise ValueError("Unsupported service {}".format(service)) + # should not reach here since service was checked in schema + return None class AWSNotify(BaseNotificationService): @@ -159,17 +129,6 @@ def __init__(self, session, aws_config): self.session = session self.aws_config = aws_config - def send_message(self, message, **kwargs): - """Send notification.""" - raise NotImplementedError("Please call async_send_message()") - - async def async_send_message(self, message="", **kwargs): - """Send notification.""" - targets = kwargs.get(ATTR_TARGET) - - if not targets: - raise HomeAssistantError("At least one target is required") - class AWSLambda(AWSNotify): """Implement the notification service for the AWS Lambda service.""" @@ -183,9 +142,11 @@ def __init__(self, session, aws_config, context): async def async_send_message(self, message="", **kwargs): """Send notification to specified LAMBDA ARN.""" - await super().async_send_message(message, **kwargs) + if not kwargs.get(ATTR_TARGET): + _LOGGER.error("At least one target is required") + return - cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) + cleaned_kwargs = {k: v for k, v in kwargs.items() if v is not None} payload = {"message": message} payload.update(cleaned_kwargs) json_payload = json.dumps(payload) @@ -214,12 +175,14 @@ class AWSSNS(AWSNotify): async def async_send_message(self, message="", **kwargs): """Send notification to specified SNS ARN.""" - await super().async_send_message(message, **kwargs) + if not kwargs.get(ATTR_TARGET): + _LOGGER.error("At least one target is required") + return message_attributes = { k: {"StringValue": json.dumps(v), "DataType": "String"} for k, v in kwargs.items() - if v + if v is not None } subject = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) @@ -248,9 +211,11 @@ class AWSSQS(AWSNotify): async def async_send_message(self, message="", **kwargs): """Send notification to specified SQS ARN.""" - await super().async_send_message(message, **kwargs) + if not kwargs.get(ATTR_TARGET): + _LOGGER.error("At least one target is required") + return - cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) + cleaned_kwargs = {k: v for k, v in kwargs.items() if v is not None} message_body = {"message": message} message_body.update(cleaned_kwargs) json_body = json.dumps(message_body) From 90c4f6f6e5057ac7e16d34d98d422176e69c1936 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Thu, 28 Mar 2019 00:24:02 +0100 Subject: [PATCH 196/605] Do data extraction in sensors (#22444) * Do data extraction in sensors * Hopefully fix lint --- .../components/netgear_lte/__init__.py | 12 +++------- .../components/netgear_lte/sensor.py | 24 +++++++++++++------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index 730c3851a2dea5..18a9e5e7c9def6 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -62,19 +62,14 @@ class ModemData: host = attr.ib() modem = attr.ib() - serial_number = attr.ib(init=False, default=None) - unread_count = attr.ib(init=False, default=None) - usage = attr.ib(init=False, default=None) + data = attr.ib(init=False, default=None) connected = attr.ib(init=False, default=True) async def async_update(self): """Call the API to update the data.""" import eternalegypt try: - information = await self.modem.information() - self.serial_number = information.serial_number - self.unread_count = sum(1 for x in information.sms if x.unread) - self.usage = information.usage + self.data = await self.modem.information() if not self.connected: _LOGGER.warning("Connected to %s", self.host) self.connected = True @@ -82,8 +77,7 @@ async def async_update(self): if self.connected: _LOGGER.warning("Lost connection to %s", self.host) self.connected = False - self.unread_count = None - self.usage = None + self.data = None async_dispatcher_send(self.hass, DISPATCHER_NETGEAR_LTE) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index a13f5fbfaa7749..1be960edfe3ad9 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -24,7 +24,7 @@ async def async_setup_platform( modem_data = hass.data[DATA_KEY].get_modem_data(discovery_info) - if not modem_data: + if not modem_data or not modem_data.data: raise PlatformNotReady sensor_conf = discovery_info[DOMAIN] @@ -47,6 +47,14 @@ class LTESensor(Entity): modem_data = attr.ib() sensor_type = attr.ib() + _unique_id = attr.ib(init=False) + + @_unique_id.default + def _init_unique_id(self): + """Register unique_id while we know data is valid.""" + return "{}_{}".format( + self.sensor_type, self.modem_data.data.serial_number) + async def async_added_to_hass(self): """Register callback.""" async_dispatcher_connect( @@ -61,10 +69,15 @@ def should_poll(self): """Return that the sensor should not be polled.""" return False + @property + def available(self): + """Return the availability of the sensor.""" + return self.modem_data.data is not None + @property def unique_id(self): """Return a unique ID like 'usage_5TG365AB0078V'.""" - return "{}_{}".format(self.sensor_type, self.modem_data.serial_number) + return self._unique_id class SMSSensor(LTESensor): @@ -78,7 +91,7 @@ def name(self): @property def state(self): """Return the state of the sensor.""" - return self.modem_data.unread_count + return sum(1 for x in self.modem_data.data.sms if x.unread) class UsageSensor(LTESensor): @@ -97,7 +110,4 @@ def name(self): @property def state(self): """Return the state of the sensor.""" - if self.modem_data.usage is None: - return None - - return round(self.modem_data.usage / 1024**2, 1) + return round(self.modem_data.data.usage / 1024**2, 1) From d8817bb12742a81ace70570997907b9f94b0a5fd Mon Sep 17 00:00:00 2001 From: Jc2k Date: Wed, 27 Mar 2019 23:36:50 +0000 Subject: [PATCH 197/605] Remove homekit_controller duplicate legacy pairing loader code (#22442) --- .../components/homekit_controller/__init__.py | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 0ed208af979e12..cf349854f52abd 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -1,6 +1,5 @@ """Support for Homekit device discovery.""" import asyncio -import json import logging import os @@ -9,6 +8,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import call_later +from .config_flow import load_old_pairings from .connection import get_accessory_information from .const import ( CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_DEVICES @@ -315,25 +315,8 @@ def setup(hass, config): hass.data[CONTROLLER] = controller = homekit.Controller() - data_dir = os.path.join(hass.config.path(), HOMEKIT_DIR) - if not os.path.isdir(data_dir): - os.mkdir(data_dir) - - pairing_file = os.path.join(data_dir, PAIRING_FILE) - if os.path.exists(pairing_file): - controller.load_data(pairing_file) - - # Migrate any existing pairings to the new internal homekit_python format - for device in os.listdir(data_dir): - if not device.startswith('hk-'): - continue - alias = device[3:] - if alias in controller.pairings: - continue - with open(os.path.join(data_dir, device)) as pairing_data_fp: - pairing_data = json.load(pairing_data_fp) - controller.pairings[alias] = IpPairing(pairing_data) - controller.save_data(pairing_file) + for hkid, pairing_data in load_old_pairings(hass).items(): + controller.pairings[hkid] = IpPairing(pairing_data) def discovery_dispatch(service, discovery_info): """Dispatcher for Homekit discovery events.""" From 217782cd05eb04e08103eeea577db12318284944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Marc=20Simard?= Date: Wed, 27 Mar 2019 20:58:23 -0400 Subject: [PATCH 198/605] Cache GTFS metadata and expose utility attributes (breaking change) (#20966) ## Description: Current sensor updates run 7 additional SQLite database queries to populate attributes, on top of the bus schedule queries themselves. Double that if you have two sensors. That leads to a lot of slowdowns for everything else when using an SD card! Considering that some data never changes (agency, routes...) and that others like departure times are good until invalidated, let's fetch such metadata at first then only when relevant changes do occur. **Breaking Change:** GTFS sensor attributes are now named using the standard snake_case format. ### Work performed: - All metadata queries are now cached. - Metadata queries are now all regrouped in the `update()` method. - Attributes assembling is now done in ~~`device_state_attributes()` where it belongs.~~ in a utility method called from `update()`, for code clarity and since there is potential I/O from SQLAlchemy. - As a bonus, many metadata entries with cryptic values have complementary entries added that provide easier to use data: - .\* Stop Drop Off Type: .\* Stop Drop Off Type **State** -> (string, unknown) - .\* Stop Pickup Type: .\* Stop Pickup Type **State** -> (string, unknown) - .\* Stop Timepoint: .\* Stop Timepoint **Exact** -> boolean - .\* Station Location Type: .\* Station Location Type **Name** -> string - .\* Wheelchair Boarding: .\* Wheelchair Boarding **Available** -> (boolean, unknown) - Route Type: Route Type **Name** (string) - Trip Bikes Allowed: Trip Bikes Allowed **State** -> (boolean, unknown) - Trip Wheelchair Access: Trip Wheelchair Access **Available** -> (boolean, unknown) - Attribute names are now using snake_case. - Added type hints. **Related issue (if applicable):** fixes #21222 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/gtfs/sensor.py | 484 ++++++++++++++++++------ 1 file changed, 369 insertions(+), 115 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 8eb5c623725afa..5555459aa16bb3 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -4,33 +4,69 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.gtfs/ """ -import os -import logging import datetime +import logging +import os import threading -from typing import Optional +from typing import Any, Callable, Optional import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_NAME, DEVICE_CLASS_TIMESTAMP -from homeassistant.helpers.entity import Entity +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_NAME, CONF_OFFSET, DEVICE_CLASS_TIMESTAMP, + STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from homeassistant.util import slugify import homeassistant.util.dt as dt_util REQUIREMENTS = ['pygtfs==0.1.5'] _LOGGER = logging.getLogger(__name__) +ATTR_ARRIVAL = 'arrival' +ATTR_BICYCLE = 'trip_bikes_allowed_state' +ATTR_DAY = 'day' +ATTR_FIRST = 'first' +ATTR_DROP_OFF_DESTINATION = 'destination_stop_drop_off_type_state' +ATTR_DROP_OFF_ORIGIN = 'origin_stop_drop_off_type_state' +ATTR_INFO = 'info' +ATTR_OFFSET = CONF_OFFSET +ATTR_LAST = 'last' +ATTR_LOCATION_DESTINATION = 'destination_station_location_type_name' +ATTR_LOCATION_ORIGIN = 'origin_station_location_type_name' +ATTR_PICKUP_DESTINATION = 'destination_stop_pickup_type_state' +ATTR_PICKUP_ORIGIN = 'origin_stop_pickup_type_state' +ATTR_ROUTE_TYPE = 'route_type_name' +ATTR_TIMEPOINT_DESTINATION = 'destination_stop_timepoint_exact' +ATTR_TIMEPOINT_ORIGIN = 'origin_stop_timepoint_exact' +ATTR_WHEELCHAIR = 'trip_wheelchair_access_available' +ATTR_WHEELCHAIR_DESTINATION = \ + 'destination_station_wheelchair_boarding_available' +ATTR_WHEELCHAIR_ORIGIN = 'origin_station_wheelchair_boarding_available' + CONF_DATA = 'data' CONF_DESTINATION = 'destination' CONF_ORIGIN = 'origin' -CONF_OFFSET = 'offset' CONF_TOMORROW = 'include_tomorrow' DEFAULT_NAME = 'GTFS Sensor' DEFAULT_PATH = 'gtfs' +BICYCLE_ALLOWED_DEFAULT = STATE_UNKNOWN +BICYCLE_ALLOWED_OPTIONS = { + 1: True, + 2: False, +} +DROP_OFF_TYPE_DEFAULT = STATE_UNKNOWN +DROP_OFF_TYPE_OPTIONS = { + 0: 'Regular', + 1: 'Not Available', + 2: 'Call Agency', + 3: 'Contact Driver', +} ICON = 'mdi:train' ICONS = { 0: 'mdi:tram', @@ -42,8 +78,47 @@ 6: 'mdi:gondola', 7: 'mdi:stairs', } +LOCATION_TYPE_DEFAULT = 'Stop' +LOCATION_TYPE_OPTIONS = { + 0: 'Station', + 1: 'Stop', + 2: "Station Entrance/Exit", + 3: 'Other', +} +PICKUP_TYPE_DEFAULT = STATE_UNKNOWN +PICKUP_TYPE_OPTIONS = { + 0: 'Regular', + 1: "None Available", + 2: "Call Agency", + 3: "Contact Driver", +} +ROUTE_TYPE_OPTIONS = { + 0: 'Tram', + 1: 'Subway', + 2: 'Rail', + 3: 'Bus', + 4: 'Ferry', + 5: "Cable Tram", + 6: "Aerial Lift", + 7: 'Funicular', +} +TIMEPOINT_DEFAULT = True +TIMEPOINT_OPTIONS = { + 0: False, + 1: True, +} +WHEELCHAIR_ACCESS_DEFAULT = STATE_UNKNOWN +WHEELCHAIR_ACCESS_OPTIONS = { + 1: True, + 2: False, +} +WHEELCHAIR_BOARDING_DEFAULT = STATE_UNKNOWN +WHEELCHAIR_BOARDING_OPTIONS = { + 1: True, + 2: False, +} -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ # type: ignore vol.Required(CONF_ORIGIN): cv.string, vol.Required(CONF_DESTINATION): cv.string, vol.Required(CONF_DATA): cv.string, @@ -53,12 +128,10 @@ }) -def get_next_departure(sched, start_station_id, end_station_id, offset, - include_tomorrow=False) -> Optional[dict]: +def get_next_departure(schedule: Any, start_station_id: Any, + end_station_id: Any, offset: cv.time_period, + include_tomorrow: cv.boolean = False) -> dict: """Get the next departure for the given schedule.""" - origin_station = sched.stops_by_id(start_station_id)[0] - destination_station = sched.stops_by_id(end_station_id)[0] - now = datetime.datetime.now() + offset now_date = now.strftime(dt_util.DATE_STR_FORMAT) yesterday = now - datetime.timedelta(days=1) @@ -84,12 +157,13 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, SELECT trip.trip_id, trip.route_id, time(origin_stop_time.arrival_time) AS origin_arrival_time, time(origin_stop_time.departure_time) AS origin_depart_time, - date(origin_stop_time.departure_time) AS origin_departure_date, + date(origin_stop_time.departure_time) AS origin_depart_date, origin_stop_time.drop_off_type AS origin_drop_off_type, origin_stop_time.pickup_type AS origin_pickup_type, origin_stop_time.shape_dist_traveled AS origin_dist_traveled, origin_stop_time.stop_headsign AS origin_stop_headsign, origin_stop_time.stop_sequence AS origin_stop_sequence, + origin_stop_time.timepoint AS origin_stop_timepoint, time(destination_stop_time.arrival_time) AS dest_arrival_time, time(destination_stop_time.departure_time) AS dest_depart_time, destination_stop_time.drop_off_type AS dest_drop_off_type, @@ -97,6 +171,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, destination_stop_time.shape_dist_traveled AS dest_dist_traveled, destination_stop_time.stop_headsign AS dest_stop_headsign, destination_stop_time.stop_sequence AS dest_stop_sequence, + destination_stop_time.timepoint AS dest_stop_timepoint, calendar.{yesterday_name} AS yesterday, calendar.{today_name} AS today, {tomorrow_select} @@ -132,11 +207,11 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, tomorrow_select=tomorrow_select, tomorrow_where=tomorrow_where, tomorrow_order=tomorrow_order) - result = sched.engine.execute(text(sql_query), - origin_station_id=origin_station.id, - end_station_id=destination_station.id, - today=now_date, - limit=limit) + result = schedule.engine.execute(text(sql_query), + origin_station_id=start_station_id, + end_station_id=end_station_id, + today=now_date, + limit=limit) # Create lookup timetable for today and possibly tomorrow, taking into # account any departures from yesterday scheduled after midnight, @@ -144,6 +219,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, timetable = {} yesterday_start = today_start = tomorrow_start = None yesterday_last = today_last = None + for row in result: if row['yesterday'] == 1 and yesterday_date >= row['start_date']: extras = { @@ -152,8 +228,8 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, 'last': False, } if yesterday_start is None: - yesterday_start = row['origin_departure_date'] - if yesterday_start != row['origin_departure_date']: + yesterday_start = row['origin_depart_date'] + if yesterday_start != row['origin_depart_date']: idx = '{} {}'.format(now_date, row['origin_depart_time']) timetable[idx] = {**row, **extras} @@ -166,9 +242,9 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, 'last': False, } if today_start is None: - today_start = row['origin_departure_date'] + today_start = row['origin_depart_date'] extras['first'] = True - if today_start == row['origin_departure_date']: + if today_start == row['origin_depart_date']: idx_prefix = now_date else: idx_prefix = tomorrow_date @@ -184,9 +260,9 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, 'last': None, } if tomorrow_start is None: - tomorrow_start = row['origin_departure_date'] + tomorrow_start = row['origin_depart_date'] extras['first'] = True - if tomorrow_start == row['origin_departure_date']: + if tomorrow_start == row['origin_depart_date']: idx = '{} {}'.format(tomorrow_date, row['origin_depart_time']) timetable[idx] = {**row, **extras} @@ -207,7 +283,7 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, break if item == {}: - return None + return {} # Format arrival and departure dates and times, accounting for the # possibility of times crossing over midnight. @@ -237,49 +313,47 @@ def get_next_departure(sched, start_station_id, end_station_id, offset, depart_time = dt_util.parse_datetime(origin_depart_time) arrival_time = dt_util.parse_datetime(dest_arrival_time) - route = sched.routes_by_id(item['route_id'])[0] - - origin_stop_time_dict = { + origin_stop_time = { 'Arrival Time': origin_arrival_time, 'Departure Time': origin_depart_time, 'Drop Off Type': item['origin_drop_off_type'], 'Pickup Type': item['origin_pickup_type'], 'Shape Dist Traveled': item['origin_dist_traveled'], 'Headsign': item['origin_stop_headsign'], - 'Sequence': item['origin_stop_sequence'] + 'Sequence': item['origin_stop_sequence'], + 'Timepoint': item['origin_stop_timepoint'], } - destination_stop_time_dict = { + destination_stop_time = { 'Arrival Time': dest_arrival_time, 'Departure Time': dest_depart_time, 'Drop Off Type': item['dest_drop_off_type'], 'Pickup Type': item['dest_pickup_type'], 'Shape Dist Traveled': item['dest_dist_traveled'], 'Headsign': item['dest_stop_headsign'], - 'Sequence': item['dest_stop_sequence'] + 'Sequence': item['dest_stop_sequence'], + 'Timepoint': item['dest_stop_timepoint'], } return { 'trip_id': item['trip_id'], + 'route_id': item['route_id'], 'day': item['day'], 'first': item['first'], 'last': item['last'], - 'trip': sched.trips_by_id(item['trip_id'])[0], - 'route': route, - 'agency': sched.agencies_by_id(route.agency_id)[0], - 'origin_station': origin_station, - 'destination_station': destination_station, 'departure_time': depart_time, 'arrival_time': arrival_time, - 'origin_stop_time': origin_stop_time_dict, - 'destination_stop_time': destination_stop_time_dict + 'origin_stop_time': origin_stop_time, + 'destination_stop_time': destination_stop_time, } -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform(hass: HomeAssistantType, config: ConfigType, + add_entities: Callable[[list], None], + discovery_info: Optional[dict] = None) -> bool: """Set up the GTFS sensor.""" gtfs_dir = hass.config.path(DEFAULT_PATH) - data = config.get(CONF_DATA) + data = str(config.get(CONF_DATA)) origin = config.get(CONF_ORIGIN) destination = config.get(CONF_DESTINATION) name = config.get(CONF_NAME) @@ -308,13 +382,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities([ GTFSDepartureSensor(gtfs, name, origin, destination, offset, include_tomorrow)]) + return True class GTFSDepartureSensor(Entity): - """Implementation of an GTFS departures sensor.""" + """Implementation of a GTFS departure sensor.""" - def __init__(self, pygtfs, name, origin, destination, offset, - include_tomorrow) -> None: + def __init__(self, pygtfs: Any, name: Optional[Any], origin: Any, + destination: Any, offset: cv.time_period, + include_tomorrow: cv.boolean) -> None: """Initialize the sensor.""" self._pygtfs = pygtfs self.origin = origin @@ -322,109 +398,287 @@ def __init__(self, pygtfs, name, origin, destination, offset, self._include_tomorrow = include_tomorrow self._offset = offset self._custom_name = name + + self._available = False self._icon = ICON self._name = '' self._state = None - self._attributes = {} + self._attributes = {} # type: dict + + self._agency = None + self._departure = {} # type: dict + self._destination = None + self._origin = None + self._route = None + self._trip = None + self.lock = threading.Lock() self.update() @property - def name(self): + def name(self) -> str: """Return the name of the sensor.""" return self._name @property - def state(self): + def state(self) -> str: """Return the state of the sensor.""" + if self._state is None: + return STATE_UNKNOWN return self._state @property - def device_state_attributes(self): + def available(self) -> bool: + """Return True if entity is available.""" + return self._available + + @property + def device_state_attributes(self) -> dict: """Return the state attributes.""" return self._attributes @property - def icon(self): + def icon(self) -> str: """Icon to use in the frontend, if any.""" return self._icon @property - def device_class(self): + def device_class(self) -> str: """Return the class of this device.""" return DEVICE_CLASS_TIMESTAMP - def update(self): + def update(self) -> None: """Get the latest data from GTFS and update the states.""" with self.lock: + # Fetch valid stop information once + if not self._origin: + stops = self._pygtfs.stops_by_id(self.origin) + if not stops: + self._available = False + _LOGGER.warning("Origin stop ID %s not found", self.origin) + return + self._origin = stops[0] + + if not self._destination: + stops = self._pygtfs.stops_by_id(self.destination) + if not stops: + self._available = False + _LOGGER.warning("Destination stop ID %s not found", + self.destination) + return + self._destination = stops[0] + + self._available = True + + # Fetch next departure self._departure = get_next_departure( self._pygtfs, self.origin, self.destination, self._offset, self._include_tomorrow) + + # Define the state as a UTC timestamp with ISO 8601 format if not self._departure: self._state = None - self._attributes = {} - self._attributes['Info'] = "No more departures" if \ - self._include_tomorrow else "No more departures today" - if self._name == '': - self._name = (self._custom_name or DEFAULT_NAME) - return - - # Define the state as a UTC timestamp with ISO 8601 format. - arrival_time = dt_util.as_utc( - self._departure['arrival_time']).isoformat() - departure_time = dt_util.as_utc( - self._departure['departure_time']).isoformat() - self._state = departure_time - - origin_station = self._departure['origin_station'] - destination_station = self._departure['destination_station'] - origin_stop_time = self._departure['origin_stop_time'] - destination_stop_time = self._departure['destination_stop_time'] - agency = self._departure['agency'] - route = self._departure['route'] - trip = self._departure['trip'] - - name = '{} {} to {} next departure' + else: + self._state = dt_util.as_utc( + self._departure['departure_time']).isoformat() + + # Fetch trip and route details once, unless updated + if not self._departure: + self._trip = None + else: + trip_id = self._departure['trip_id'] + if not self._trip or self._trip.trip_id != trip_id: + _LOGGER.info("Fetching trip details for %s", trip_id) + self._trip = self._pygtfs.trips_by_id(trip_id)[0] + + route_id = self._departure['route_id'] + if not self._route or self._route.route_id != route_id: + _LOGGER.info("Fetching route details for %s", route_id) + self._route = self._pygtfs.routes_by_id(route_id)[0] + + # Fetch agency details exactly once + if self._agency is None and self._route: + try: + _LOGGER.info("Fetching agency details for %s", + self._route.agency_id) + self._agency = self._pygtfs.agencies_by_id( + self._route.agency_id)[0] + except IndexError: + _LOGGER.warning( + "Agency ID '%s' not found in agency table. You may " + "want to update the agency database table to fix this " + "missing reference.", self._route.agency_id) + self._agency = False + + # Assign attributes, icon and name + self.update_attributes() + + if self._route: + self._icon = ICONS.get(self._route.route_type, ICON) + else: + self._icon = ICON + + name = '{agency} {origin} to {destination} next departure' + if not self._departure: + name = '{default}' self._name = (self._custom_name or - name.format(agency.agency_name, - origin_station.stop_id, - destination_station.stop_id)) - - self._icon = ICONS.get(route.route_type, ICON) - - # Build attributes - self._attributes['arrival'] = arrival_time - self._attributes['day'] = self._departure['day'] - if self._departure['first'] is not None: - self._attributes['first'] = self._departure['first'] - if self._departure['last'] is not None: - self._attributes['last'] = self._departure['last'] - self._attributes['offset'] = self._offset.seconds / 60 - - def dict_for_table(resource): - """Return a dict for the SQLAlchemy resource given.""" - return dict((col, getattr(resource, col)) - for col in resource.__table__.columns.keys()) - - def append_keys(resource, prefix=None): - """Properly format key val pairs to append to attributes.""" - for key, val in resource.items(): - if val == "" or val is None or key == 'feed_id': - continue - pretty_key = key.replace('_', ' ') - pretty_key = pretty_key.title() - pretty_key = pretty_key.replace('Id', 'ID') - pretty_key = pretty_key.replace('Url', 'URL') - if prefix is not None and \ - pretty_key.startswith(prefix) is False: - pretty_key = '{} {}'.format(prefix, pretty_key) - self._attributes[pretty_key] = val - - append_keys(dict_for_table(agency), 'Agency') - append_keys(dict_for_table(route), 'Route') - append_keys(dict_for_table(trip), 'Trip') - append_keys(dict_for_table(origin_station), 'Origin Station') - append_keys(dict_for_table(destination_station), - 'Destination Station') - append_keys(origin_stop_time, 'Origin Stop') - append_keys(destination_stop_time, 'Destination Stop') + name.format(agency=getattr(self._agency, + 'agency_name', + DEFAULT_NAME), + default=DEFAULT_NAME, + origin=self.origin, + destination=self.destination)) + + def update_attributes(self) -> None: + """Update state attributes.""" + # Add departure information + if self._departure: + self._attributes[ATTR_ARRIVAL] = dt_util.as_utc( + self._departure['arrival_time']).isoformat() + + self._attributes[ATTR_DAY] = self._departure['day'] + + if self._departure[ATTR_FIRST] is not None: + self._attributes[ATTR_FIRST] = self._departure['first'] + elif ATTR_FIRST in self._attributes.keys(): + del self._attributes[ATTR_FIRST] + + if self._departure[ATTR_LAST] is not None: + self._attributes[ATTR_LAST] = self._departure['last'] + elif ATTR_LAST in self._attributes.keys(): + del self._attributes[ATTR_LAST] + else: + if ATTR_ARRIVAL in self._attributes.keys(): + del self._attributes[ATTR_ARRIVAL] + if ATTR_DAY in self._attributes.keys(): + del self._attributes[ATTR_DAY] + if ATTR_FIRST in self._attributes.keys(): + del self._attributes[ATTR_FIRST] + if ATTR_LAST in self._attributes.keys(): + del self._attributes[ATTR_LAST] + + # Add contextual information + self._attributes[ATTR_OFFSET] = self._offset.seconds / 60 + + if self._state is None: + self._attributes[ATTR_INFO] = "No more departures" if \ + self._include_tomorrow else "No more departures today" + elif ATTR_INFO in self._attributes.keys(): + del self._attributes[ATTR_INFO] + + if self._agency: + self._attributes[ATTR_ATTRIBUTION] = self._agency.agency_name + elif ATTR_ATTRIBUTION in self._attributes.keys(): + del self._attributes[ATTR_ATTRIBUTION] + + # Add extra metadata + key = 'agency_id' + if self._agency and key not in self._attributes.keys(): + self.append_keys(self.dict_for_table(self._agency), 'Agency') + + key = 'origin_station_stop_id' + if self._origin and key not in self._attributes.keys(): + self.append_keys(self.dict_for_table(self._origin), + "Origin Station") + self._attributes[ATTR_LOCATION_ORIGIN] = \ + LOCATION_TYPE_OPTIONS.get( + self._origin.location_type, + LOCATION_TYPE_DEFAULT) + self._attributes[ATTR_WHEELCHAIR_ORIGIN] = \ + WHEELCHAIR_BOARDING_OPTIONS.get( + self._origin.wheelchair_boarding, + WHEELCHAIR_BOARDING_DEFAULT) + + key = 'destination_station_stop_id' + if self._destination and key not in self._attributes.keys(): + self.append_keys(self.dict_for_table(self._destination), + "Destination Station") + self._attributes[ATTR_LOCATION_DESTINATION] = \ + LOCATION_TYPE_OPTIONS.get( + self._destination.location_type, + LOCATION_TYPE_DEFAULT) + self._attributes[ATTR_WHEELCHAIR_DESTINATION] = \ + WHEELCHAIR_BOARDING_OPTIONS.get( + self._destination.wheelchair_boarding, + WHEELCHAIR_BOARDING_DEFAULT) + + # Manage Route metadata + key = 'route_id' + if not self._route and key in self._attributes.keys(): + self.remove_keys('Route') + elif self._route and (key not in self._attributes.keys() or + self._attributes[key] != self._route.route_id): + self.append_keys(self.dict_for_table(self._route), 'Route') + self._attributes[ATTR_ROUTE_TYPE] = \ + ROUTE_TYPE_OPTIONS[self._route.route_type] + + # Manage Trip metadata + key = 'trip_id' + if not self._trip and key in self._attributes.keys(): + self.remove_keys('Trip') + elif self._trip and (key not in self._attributes.keys() or + self._attributes[key] != self._trip.trip_id): + self.append_keys(self.dict_for_table(self._trip), 'Trip') + self._attributes[ATTR_BICYCLE] = BICYCLE_ALLOWED_OPTIONS.get( + self._trip.bikes_allowed, + BICYCLE_ALLOWED_DEFAULT) + self._attributes[ATTR_WHEELCHAIR] = WHEELCHAIR_ACCESS_OPTIONS.get( + self._trip.wheelchair_accessible, + WHEELCHAIR_ACCESS_DEFAULT) + + # Manage Stop Times metadata + prefix = 'origin_stop' + if self._departure: + self.append_keys(self._departure['origin_stop_time'], prefix) + self._attributes[ATTR_DROP_OFF_ORIGIN] = DROP_OFF_TYPE_OPTIONS.get( + self._departure['origin_stop_time']['Drop Off Type'], + DROP_OFF_TYPE_DEFAULT) + self._attributes[ATTR_PICKUP_ORIGIN] = PICKUP_TYPE_OPTIONS.get( + self._departure['origin_stop_time']['Pickup Type'], + PICKUP_TYPE_DEFAULT) + self._attributes[ATTR_TIMEPOINT_ORIGIN] = TIMEPOINT_OPTIONS.get( + self._departure['origin_stop_time']['Timepoint'], + TIMEPOINT_DEFAULT) + else: + self.remove_keys(prefix) + + prefix = 'destination_stop' + if self._departure: + self.append_keys(self._departure['destination_stop_time'], prefix) + self._attributes[ATTR_DROP_OFF_DESTINATION] = \ + DROP_OFF_TYPE_OPTIONS.get( + self._departure['destination_stop_time']['Drop Off Type'], + DROP_OFF_TYPE_DEFAULT) + self._attributes[ATTR_PICKUP_DESTINATION] = \ + PICKUP_TYPE_OPTIONS.get( + self._departure['destination_stop_time']['Pickup Type'], + PICKUP_TYPE_DEFAULT) + self._attributes[ATTR_TIMEPOINT_DESTINATION] = \ + TIMEPOINT_OPTIONS.get( + self._departure['destination_stop_time']['Timepoint'], + TIMEPOINT_DEFAULT) + else: + self.remove_keys(prefix) + + @staticmethod + def dict_for_table(resource: Any) -> dict: + """Return a dictionary for the SQLAlchemy resource given.""" + return dict((col, getattr(resource, col)) + for col in resource.__table__.columns.keys()) + + def append_keys(self, resource: dict, prefix: Optional[str] = None) -> \ + None: + """Properly format key val pairs to append to attributes.""" + for attr, val in resource.items(): + if val == '' or val is None or attr == 'feed_id': + continue + key = attr + if prefix and not key.startswith(prefix): + key = '{} {}'.format(prefix, key) + key = slugify(key) + self._attributes[key] = val + + def remove_keys(self, prefix: str) -> None: + """Remove attributes whose key starts with prefix.""" + self._attributes = {k: v for k, v in self._attributes.items() if + not k.startswith(prefix)} From 9176e13a97b57aa062d308acf09148b8baf911b5 Mon Sep 17 00:00:00 2001 From: dilruacs Date: Thu, 28 Mar 2019 02:19:24 +0100 Subject: [PATCH 199/605] Modify check for ADB public key (#22378) * Remove check for public key * Remove has_adb_files, directly call cv.isfile * Check for missing adbkey.pub, create dummy if not found * Reorder imports * Bumped androidtv library version, deactivated pubkey test * Code works without pubkey, removed function * Removed "import os", not needed anymore * Bump library version --- homeassistant/components/androidtv/media_player.py | 12 ++---------- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 2db3110f2d9d46..5bce21f05a0b20 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -22,7 +22,7 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.13'] +REQUIREMENTS = ['androidtv==0.0.14'] _LOGGER = logging.getLogger(__name__) @@ -61,21 +61,13 @@ }) -def has_adb_files(value): - """Check that ADB key files exist.""" - priv_key = value - pub_key = '{}.pub'.format(value) - cv.isfile(pub_key) - return cv.isfile(priv_key) - - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): vol.In(DEVICE_CLASSES), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_ADBKEY): has_adb_files, + vol.Optional(CONF_ADBKEY): cv.isfile, vol.Optional(CONF_ADB_SERVER_IP): cv.string, vol.Optional(CONF_ADB_SERVER_PORT, default=DEFAULT_ADB_SERVER_PORT): cv.port, diff --git a/requirements_all.txt b/requirements_all.txt index 2e66b5b39e192b..2b993100f58a18 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -158,7 +158,7 @@ alpha_vantage==2.1.0 amcrest==1.2.7 # homeassistant.components.androidtv.media_player -androidtv==0.0.13 +androidtv==0.0.14 # homeassistant.components.anel_pwrctrl.switch anel_pwrctrl-homeassistant==0.0.1.dev2 From 78e162c1d32f1bf9ceeb8a3db3edde67f0690989 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 27 Mar 2019 19:48:05 -0700 Subject: [PATCH 200/605] Centralize all usages of `host` as a configuration param to the constant CONF_HOST (#22458) * Centralize all usages of as a configuration param to the constant CONF_HOST * Clean up test --- .../components/alarmdecoder/__init__.py | 7 +++---- homeassistant/components/daikin/__init__.py | 7 +++---- .../components/daikin/config_flow.py | 11 +++++----- homeassistant/components/daikin/const.py | 1 - .../components/envisalink/__init__.py | 8 +++---- homeassistant/components/nad/media_player.py | 3 +-- .../components/ness_alarm/__init__.py | 7 +++---- .../components/notify/nfandroidtv.py | 7 +++---- .../components/satel_integra/__init__.py | 7 +++---- .../components/tellduslive/__init__.py | 6 +++--- .../components/tellduslive/config_flow.py | 21 ++++++++++--------- homeassistant/components/tellduslive/const.py | 1 - .../components/tradfri/config_flow.py | 5 ++--- tests/components/daikin/test_config_flow.py | 17 ++++++++------- tests/components/ness_alarm/test_init.py | 6 +++--- .../tellduslive/test_config_flow.py | 13 ++++++------ 16 files changed, 61 insertions(+), 66 deletions(-) diff --git a/homeassistant/components/alarmdecoder/__init__.py b/homeassistant/components/alarmdecoder/__init__.py index 1f74d72809b294..5b1296b39de2c3 100644 --- a/homeassistant/components/alarmdecoder/__init__.py +++ b/homeassistant/components/alarmdecoder/__init__.py @@ -5,7 +5,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_HOST from homeassistant.helpers.discovery import load_platform from homeassistant.util import dt as dt_util from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA @@ -20,7 +20,6 @@ CONF_DEVICE = 'device' CONF_DEVICE_BAUD = 'baudrate' -CONF_DEVICE_HOST = 'host' CONF_DEVICE_PATH = 'path' CONF_DEVICE_PORT = 'port' CONF_DEVICE_TYPE = 'type' @@ -55,7 +54,7 @@ DEVICE_SOCKET_SCHEMA = vol.Schema({ vol.Required(CONF_DEVICE_TYPE): 'socket', - vol.Optional(CONF_DEVICE_HOST, default=DEFAULT_DEVICE_HOST): cv.string, + vol.Optional(CONF_HOST, default=DEFAULT_DEVICE_HOST): cv.string, vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_DEVICE_PORT): cv.port}) DEVICE_SERIAL_SCHEMA = vol.Schema({ @@ -165,7 +164,7 @@ def handle_rel_message(sender, message): controller = False if device_type == 'socket': - host = device.get(CONF_DEVICE_HOST) + host = device.get(CONF_HOST) port = device.get(CONF_DEVICE_PORT) controller = AlarmDecoder(SocketDevice(interface=(host, port))) elif device_type == 'serial': diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index a09991e3b7225f..5ad21f5954f3b6 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -8,14 +8,13 @@ import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import CONF_HOSTS +from homeassistant.const import CONF_HOSTS, CONF_HOST import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util import Throttle from . import config_flow # noqa pylint_disable=unused-import -from .const import KEY_HOST REQUIREMENTS = ['pydaikin==1.3.1'] @@ -53,7 +52,7 @@ async def async_setup(hass, config): DOMAIN, context={'source': SOURCE_IMPORT}, data={ - KEY_HOST: host, + CONF_HOST: host, })) return True @@ -61,7 +60,7 @@ async def async_setup(hass, config): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Establish connection with Daikin.""" conf = entry.data - daikin_api = await daikin_api_setup(hass, conf[KEY_HOST]) + daikin_api = await daikin_api_setup(hass, conf[CONF_HOST]) if not daikin_api: return False hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api}) diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index b46e800c3d8765..590b5a027387a7 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -6,8 +6,9 @@ import voluptuous as vol from homeassistant import config_entries +from homeassistant.const import CONF_HOST -from .const import KEY_HOST, KEY_IP, KEY_MAC +from .const import KEY_IP, KEY_MAC _LOGGER = logging.getLogger(__name__) @@ -29,7 +30,7 @@ async def _create_entry(self, host, mac): return self.async_create_entry( title=host, data={ - KEY_HOST: host, + CONF_HOST: host, KEY_MAC: mac }) @@ -55,14 +56,14 @@ async def async_step_user(self, user_input=None): return self.async_show_form( step_id='user', data_schema=vol.Schema({ - vol.Required(KEY_HOST): str + vol.Required(CONF_HOST): str }) ) - return await self._create_device(user_input[KEY_HOST]) + return await self._create_device(user_input[CONF_HOST]) async def async_step_import(self, user_input): """Import a config entry.""" - host = user_input.get(KEY_HOST) + host = user_input.get(CONF_HOST) if not host: return await self.async_step_user() return await self._create_device(host) diff --git a/homeassistant/components/daikin/const.py b/homeassistant/components/daikin/const.py index cd2742b5b5e060..90967904579c70 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -20,6 +20,5 @@ } } -KEY_HOST = 'host' KEY_MAC = 'mac' KEY_IP = 'ip' diff --git a/homeassistant/components/envisalink/__init__.py b/homeassistant/components/envisalink/__init__.py index b7590341f788c3..c46a26c6f857f3 100644 --- a/homeassistant/components/envisalink/__init__.py +++ b/homeassistant/components/envisalink/__init__.py @@ -6,7 +6,8 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_TIMEOUT +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_TIMEOUT, \ + CONF_HOST from homeassistant.helpers.entity import Entity from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -20,7 +21,6 @@ DATA_EVL = 'envisalink' CONF_CODE = 'code' -CONF_EVL_HOST = 'host' CONF_EVL_KEEPALIVE = 'keepalive_interval' CONF_EVL_PORT = 'port' CONF_EVL_VERSION = 'evl_version' @@ -56,7 +56,7 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_EVL_HOST): cv.string, + vol.Required(CONF_HOST): cv.string, vol.Required(CONF_PANEL_TYPE): vol.All(cv.string, vol.In(['HONEYWELL', 'DSC'])), vol.Required(CONF_USERNAME): cv.string, @@ -95,7 +95,7 @@ async def async_setup(hass, config): conf = config.get(DOMAIN) - host = conf.get(CONF_EVL_HOST) + host = conf.get(CONF_HOST) port = conf.get(CONF_EVL_PORT) code = conf.get(CONF_CODE) panel_type = conf.get(CONF_PANEL_TYPE) diff --git a/homeassistant/components/nad/media_player.py b/homeassistant/components/nad/media_player.py index 127be02dac4aa3..00738abe4d1472 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player.const import ( SUPPORT_SELECT_SOURCE, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) -from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON +from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, CONF_HOST REQUIREMENTS = ['nad_receiver==0.0.11'] @@ -34,7 +34,6 @@ CONF_TYPE = 'type' CONF_SERIAL_PORT = 'serial_port' # for NADReceiver -CONF_HOST = 'host' # for NADReceiverTelnet CONF_PORT = 'port' # for NADReceiverTelnet CONF_MIN_VOLUME = 'min_volume' CONF_MAX_VOLUME = 'max_volume' diff --git a/homeassistant/components/ness_alarm/__init__.py b/homeassistant/components/ness_alarm/__init__.py index 4e8c8293c2d25a..97896f9aa3f9ec 100644 --- a/homeassistant/components/ness_alarm/__init__.py +++ b/homeassistant/components/ness_alarm/__init__.py @@ -8,7 +8,7 @@ from homeassistant.components.binary_sensor import DEVICE_CLASSES from homeassistant.const import (ATTR_CODE, ATTR_STATE, EVENT_HOMEASSISTANT_STOP, - CONF_SCAN_INTERVAL) + CONF_SCAN_INTERVAL, CONF_HOST) from homeassistant.helpers import config_validation as cv from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -20,7 +20,6 @@ DOMAIN = 'ness_alarm' DATA_NESS = 'ness_alarm' -CONF_DEVICE_HOST = 'host' CONF_DEVICE_PORT = 'port' CONF_INFER_ARMING_STATE = 'infer_arming_state' CONF_ZONES = 'zones' @@ -46,7 +45,7 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_DEVICE_HOST): cv.string, + vol.Required(CONF_HOST): cv.string, vol.Required(CONF_DEVICE_PORT): cv.port, vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): vol.All(cv.time_period, cv.positive_timedelta), @@ -76,7 +75,7 @@ async def async_setup(hass, config): conf = config[DOMAIN] zones = conf[CONF_ZONES] - host = conf[CONF_DEVICE_HOST] + host = conf[CONF_HOST] port = conf[CONF_DEVICE_PORT] scan_interval = conf[CONF_SCAN_INTERVAL] infer_arming_state = conf[CONF_INFER_ARMING_STATE] diff --git a/homeassistant/components/notify/nfandroidtv.py b/homeassistant/components/notify/nfandroidtv.py index 8127b4b3e967a3..4d39083387c1ed 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/notify/nfandroidtv.py @@ -12,7 +12,7 @@ from requests.auth import HTTPBasicAuth, HTTPDigestAuth import voluptuous as vol -from homeassistant.const import CONF_TIMEOUT +from homeassistant.const import CONF_TIMEOUT, CONF_HOST import homeassistant.helpers.config_validation as cv from . import ( @@ -21,7 +21,6 @@ _LOGGER = logging.getLogger(__name__) -CONF_IP = 'host' CONF_DURATION = 'duration' CONF_FONTSIZE = 'fontsize' CONF_POSITION = 'position' @@ -95,7 +94,7 @@ } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_IP): cv.string, + vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_DURATION, default=DEFAULT_DURATION): vol.Coerce(int), vol.Optional(CONF_FONTSIZE, default=DEFAULT_FONTSIZE): vol.In(FONTSIZES.keys()), @@ -112,7 +111,7 @@ def get_service(hass, config, discovery_info=None): """Get the Notifications for Android TV notification service.""" - remoteip = config.get(CONF_IP) + remoteip = config.get(CONF_HOST) duration = config.get(CONF_DURATION) fontsize = config.get(CONF_FONTSIZE) position = config.get(CONF_POSITION) diff --git a/homeassistant/components/satel_integra/__init__.py b/homeassistant/components/satel_integra/__init__.py index 93f157cd5ec1bf..5f5c43d961f552 100644 --- a/homeassistant/components/satel_integra/__init__.py +++ b/homeassistant/components/satel_integra/__init__.py @@ -3,7 +3,7 @@ import voluptuous as vol -from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_HOST from homeassistant.core import callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.discovery import async_load_platform @@ -23,7 +23,6 @@ DATA_SATEL = 'satel_integra' -CONF_DEVICE_HOST = 'host' CONF_DEVICE_PORT = 'port' CONF_DEVICE_PARTITION = 'partition' CONF_ARM_HOME_MODE = 'arm_home_mode' @@ -48,7 +47,7 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_DEVICE_HOST): cv.string, + vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_DEVICE_PARTITION, default=DEFAULT_DEVICE_PARTITION): cv.positive_int, @@ -68,7 +67,7 @@ async def async_setup(hass, config): zones = conf.get(CONF_ZONES) outputs = conf.get(CONF_OUTPUTS) - host = conf.get(CONF_DEVICE_HOST) + host = conf.get(CONF_HOST) port = conf.get(CONF_DEVICE_PORT) partition = conf.get(CONF_DEVICE_PARTITION) diff --git a/homeassistant/components/tellduslive/__init__.py b/homeassistant/components/tellduslive/__init__.py index 397e21922d91d6..6a6b18557b0598 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -14,7 +14,7 @@ from . import config_flow # noqa pylint_disable=unused-import from .const import ( - CONF_HOST, DOMAIN, KEY_HOST, KEY_SCAN_INTERVAL, KEY_SESSION, + CONF_HOST, DOMAIN, KEY_SCAN_INTERVAL, KEY_SESSION, MIN_UPDATE_INTERVAL, NOT_SO_PRIVATE_KEY, PUBLIC_KEY, SCAN_INTERVAL, SIGNAL_UPDATE_ENTITY, TELLDUS_DISCOVERY_NEW) @@ -54,7 +54,7 @@ async def async_setup_entry(hass, entry): from tellduslive import Session conf = entry.data[KEY_SESSION] - if KEY_HOST in conf: + if CONF_HOST in conf: # Session(**conf) does blocking IO when # communicating with local devices. session = await hass.async_add_executor_job(partial(Session, **conf)) @@ -108,7 +108,7 @@ async def async_setup(hass, config): DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, data={ - KEY_HOST: config[DOMAIN].get(CONF_HOST), + CONF_HOST: config[DOMAIN].get(CONF_HOST), KEY_SCAN_INTERVAL: config[DOMAIN][CONF_SCAN_INTERVAL], })) return True diff --git a/homeassistant/components/tellduslive/config_flow.py b/homeassistant/components/tellduslive/config_flow.py index 62463bc0a9ea39..ff02419d624b20 100644 --- a/homeassistant/components/tellduslive/config_flow.py +++ b/homeassistant/components/tellduslive/config_flow.py @@ -7,10 +7,11 @@ import voluptuous as vol from homeassistant import config_entries +from homeassistant.const import CONF_HOST from homeassistant.util.json import load_json from .const import ( - APPLICATION_NAME, CLOUD_NAME, DOMAIN, KEY_HOST, KEY_SCAN_INTERVAL, + APPLICATION_NAME, CLOUD_NAME, DOMAIN, KEY_SCAN_INTERVAL, KEY_SESSION, NOT_SO_PRIVATE_KEY, PUBLIC_KEY, SCAN_INTERVAL, TELLDUS_CONFIG_FILE) @@ -50,14 +51,14 @@ async def async_step_user(self, user_input=None): return self.async_abort(reason='already_setup') if user_input is not None or len(self._hosts) == 1: - if user_input is not None and user_input[KEY_HOST] != CLOUD_NAME: - self._host = user_input[KEY_HOST] + if user_input is not None and user_input[CONF_HOST] != CLOUD_NAME: + self._host = user_input[CONF_HOST] return await self.async_step_auth() return self.async_show_form( step_id='user', data_schema=vol.Schema({ - vol.Required(KEY_HOST): + vol.Required(CONF_HOST): vol.In(list(self._hosts)) })) @@ -70,7 +71,7 @@ async def async_step_auth(self, user_input=None): host = self._host or CLOUD_NAME if self._host: session = { - KEY_HOST: host, + CONF_HOST: host, KEY_TOKEN: self._session.access_token } else: @@ -80,7 +81,7 @@ async def async_step_auth(self, user_input=None): } return self.async_create_entry( title=host, data={ - KEY_HOST: host, + CONF_HOST: host, KEY_SCAN_INTERVAL: self._scan_interval.seconds, KEY_SESSION: session, }) @@ -124,8 +125,8 @@ async def async_step_import(self, user_input): return self.async_abort(reason='already_setup') self._scan_interval = user_input[KEY_SCAN_INTERVAL] - if user_input[KEY_HOST] != DOMAIN: - self._hosts.append(user_input[KEY_HOST]) + if user_input[CONF_HOST] != DOMAIN: + self._hosts.append(user_input[CONF_HOST]) if not await self.hass.async_add_executor_job( os.path.isfile, self.hass.config.path(TELLDUS_CONFIG_FILE)): @@ -135,14 +136,14 @@ async def async_step_import(self, user_input): load_json, self.hass.config.path(TELLDUS_CONFIG_FILE)) host = next(iter(conf)) - if user_input[KEY_HOST] != host: + if user_input[CONF_HOST] != host: return await self.async_step_user() host = CLOUD_NAME if host == 'tellduslive' else host return self.async_create_entry( title=host, data={ - KEY_HOST: host, + CONF_HOST: host, KEY_SCAN_INTERVAL: self._scan_interval.seconds, KEY_SESSION: next(iter(conf.values())), }) diff --git a/homeassistant/components/tellduslive/const.py b/homeassistant/components/tellduslive/const.py index 80b0513b763e26..7898cd8b1f697a 100644 --- a/homeassistant/components/tellduslive/const.py +++ b/homeassistant/components/tellduslive/const.py @@ -13,7 +13,6 @@ SIGNAL_UPDATE_ENTITY = 'tellduslive_update' -KEY_HOST = 'host' KEY_SESSION = 'session' KEY_SCAN_INTERVAL = 'scan_interval' diff --git a/homeassistant/components/tradfri/config_flow.py b/homeassistant/components/tradfri/config_flow.py index 2e24fde8294d27..0ad269b8780be1 100644 --- a/homeassistant/components/tradfri/config_flow.py +++ b/homeassistant/components/tradfri/config_flow.py @@ -11,7 +11,6 @@ from .const import ( CONF_IMPORT_GROUPS, CONF_IDENTITY, CONF_HOST, CONF_KEY, CONF_GATEWAY_ID) -KEY_HOST = 'host' KEY_SECURITY_CODE = 'security_code' KEY_IMPORT_GROUPS = 'import_groups' @@ -45,7 +44,7 @@ async def async_step_auth(self, user_input=None): errors = {} if user_input is not None: - host = user_input.get(KEY_HOST, self._host) + host = user_input.get(CONF_HOST, self._host) try: auth = await authenticate( self.hass, host, @@ -67,7 +66,7 @@ async def async_step_auth(self, user_input=None): fields = OrderedDict() if self._host is None: - fields[vol.Required(KEY_HOST)] = str + fields[vol.Required(CONF_HOST)] = str fields[vol.Required(KEY_SECURITY_CODE)] = str diff --git a/tests/components/daikin/test_config_flow.py b/tests/components/daikin/test_config_flow.py index f4ad676b9aa13c..f6b2f0b1d41990 100644 --- a/tests/components/daikin/test_config_flow.py +++ b/tests/components/daikin/test_config_flow.py @@ -6,7 +6,8 @@ from homeassistant import data_entry_flow from homeassistant.components.daikin import config_flow -from homeassistant.components.daikin.const import KEY_HOST, KEY_IP, KEY_MAC +from homeassistant.components.daikin.const import KEY_IP, KEY_MAC +from homeassistant.const import CONF_HOST from tests.common import MockConfigEntry, MockDependency @@ -37,10 +38,10 @@ async def test_user(hass, mock_daikin): assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'user' - result = await flow.async_step_user({KEY_HOST: HOST}) + result = await flow.async_step_user({CONF_HOST: HOST}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['title'] == HOST - assert result['data'][KEY_HOST] == HOST + assert result['data'][CONF_HOST] == HOST assert result['data'][KEY_MAC] == MAC @@ -49,7 +50,7 @@ async def test_abort_if_already_setup(hass, mock_daikin): flow = init_config_flow(hass) MockConfigEntry(domain='daikin', data={KEY_MAC: MAC}).add_to_hass(hass) - result = await flow.async_step_user({KEY_HOST: HOST}) + result = await flow.async_step_user({CONF_HOST: HOST}) assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT assert result['reason'] == 'already_configured' @@ -62,10 +63,10 @@ async def test_import(hass, mock_daikin): assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'user' - result = await flow.async_step_import({KEY_HOST: HOST}) + result = await flow.async_step_import({CONF_HOST: HOST}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['title'] == HOST - assert result['data'][KEY_HOST] == HOST + assert result['data'][CONF_HOST] == HOST assert result['data'][KEY_MAC] == MAC @@ -76,7 +77,7 @@ async def test_discovery(hass, mock_daikin): result = await flow.async_step_discovery({KEY_IP: HOST, KEY_MAC: MAC}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['title'] == HOST - assert result['data'][KEY_HOST] == HOST + assert result['data'][CONF_HOST] == HOST assert result['data'][KEY_MAC] == MAC @@ -88,6 +89,6 @@ async def test_device_abort(hass, mock_daikin, s_effect, reason): flow = init_config_flow(hass) mock_daikin.Appliance.side_effect = s_effect - result = await flow.async_step_user({KEY_HOST: HOST}) + result = await flow.async_step_user({CONF_HOST: HOST}) assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT assert result['reason'] == reason diff --git a/tests/components/ness_alarm/test_init.py b/tests/components/ness_alarm/test_init.py index 7d2104c9306c67..534f055dbad798 100644 --- a/tests/components/ness_alarm/test_init.py +++ b/tests/components/ness_alarm/test_init.py @@ -6,20 +6,20 @@ from homeassistant.components import alarm_control_panel from homeassistant.components.ness_alarm import ( - DOMAIN, CONF_DEVICE_PORT, CONF_DEVICE_HOST, CONF_ZONE_NAME, CONF_ZONES, + DOMAIN, CONF_DEVICE_PORT, CONF_ZONE_NAME, CONF_ZONES, CONF_ZONE_ID, SERVICE_AUX, SERVICE_PANIC, ATTR_CODE, ATTR_OUTPUT_ID) from homeassistant.const import ( STATE_ALARM_ARMING, SERVICE_ALARM_DISARM, ATTR_ENTITY_ID, SERVICE_ALARM_ARM_AWAY, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_TRIGGER, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, STATE_ALARM_PENDING, - STATE_ALARM_TRIGGERED, STATE_UNKNOWN) + STATE_ALARM_TRIGGERED, STATE_UNKNOWN, CONF_HOST) from homeassistant.setup import async_setup_component from tests.common import MockDependency VALID_CONFIG = { DOMAIN: { - CONF_DEVICE_HOST: 'alarm.local', + CONF_HOST: 'alarm.local', CONF_DEVICE_PORT: 1234, CONF_ZONES: [ { diff --git a/tests/components/tellduslive/test_config_flow.py b/tests/components/tellduslive/test_config_flow.py index 5be9a03742e3ca..460b33e5cd77ed 100644 --- a/tests/components/tellduslive/test_config_flow.py +++ b/tests/components/tellduslive/test_config_flow.py @@ -7,8 +7,9 @@ from homeassistant import data_entry_flow from homeassistant.components.tellduslive import ( - APPLICATION_NAME, DOMAIN, KEY_HOST, KEY_SCAN_INTERVAL, SCAN_INTERVAL, + APPLICATION_NAME, DOMAIN, KEY_SCAN_INTERVAL, SCAN_INTERVAL, config_flow) +from homeassistant.const import CONF_HOST from tests.common import MockConfigEntry, MockDependency, mock_coro @@ -94,7 +95,7 @@ async def test_step_import(hass, mock_tellduslive): flow = init_config_flow(hass) result = await flow.async_step_import({ - KEY_HOST: DOMAIN, + CONF_HOST: DOMAIN, KEY_SCAN_INTERVAL: 0, }) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM @@ -106,7 +107,7 @@ async def test_step_import_add_host(hass, mock_tellduslive): flow = init_config_flow(hass) result = await flow.async_step_import({ - KEY_HOST: 'localhost', + CONF_HOST: 'localhost', KEY_SCAN_INTERVAL: 0, }) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM @@ -117,7 +118,7 @@ async def test_step_import_no_config_file(hass, mock_tellduslive): """Test that we trigger user with no config_file configuring from import.""" flow = init_config_flow(hass) - result = await flow.async_step_import({ KEY_HOST: 'localhost', KEY_SCAN_INTERVAL: 0, }) + result = await flow.async_step_import({ CONF_HOST: 'localhost', KEY_SCAN_INTERVAL: 0, }) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'user' @@ -129,7 +130,7 @@ async def test_step_import_load_json_matching_host(hass, mock_tellduslive): with patch('homeassistant.components.tellduslive.config_flow.load_json', return_value={'tellduslive': {}}), \ patch('os.path.isfile'): - result = await flow.async_step_import({ KEY_HOST: 'Cloud API', KEY_SCAN_INTERVAL: 0, }) + result = await flow.async_step_import({ CONF_HOST: 'Cloud API', KEY_SCAN_INTERVAL: 0, }) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'user' @@ -141,7 +142,7 @@ async def test_step_import_load_json(hass, mock_tellduslive): with patch('homeassistant.components.tellduslive.config_flow.load_json', return_value={'localhost': {}}), \ patch('os.path.isfile'): - result = await flow.async_step_import({ KEY_HOST: 'localhost', KEY_SCAN_INTERVAL: SCAN_INTERVAL, }) + result = await flow.async_step_import({ CONF_HOST: 'localhost', KEY_SCAN_INTERVAL: SCAN_INTERVAL, }) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['title'] == 'localhost' assert result['data']['host'] == 'localhost' From e26a5abb2bd542f2513196126f3d285d61f86cf0 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Wed, 27 Mar 2019 21:50:02 -0500 Subject: [PATCH 201/605] Don't return cover position when not supported (#22484) --- homeassistant/components/smartthings/cover.py | 2 ++ tests/components/smartthings/test_cover.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/smartthings/cover.py b/homeassistant/components/smartthings/cover.py index 131da75f4febab..53602c3643c2d4 100644 --- a/homeassistant/components/smartthings/cover.py +++ b/homeassistant/components/smartthings/cover.py @@ -135,6 +135,8 @@ def is_closed(self): @property def current_cover_position(self): """Return current position of cover.""" + if not self._supported_features & SUPPORT_SET_POSITION: + return None return self._device.status.level @property diff --git a/tests/components/smartthings/test_cover.py b/tests/components/smartthings/test_cover.py index 7e41237e3e70f9..fb90882eae8742 100644 --- a/tests/components/smartthings/test_cover.py +++ b/tests/components/smartthings/test_cover.py @@ -142,7 +142,10 @@ async def test_set_cover_position_unsupported(hass, device_factory): COVER_DOMAIN, SERVICE_SET_COVER_POSITION, {ATTR_POSITION: 50}, blocking=True) - # Ensure API was notcalled + state = hass.states.get('cover.shade') + assert ATTR_CURRENT_POSITION not in state.attributes + + # Ensure API was not called # pylint: disable=protected-access assert device._api.post_device_command.call_count == 0 # type: ignore From e670491c8630488a26675ebe98d8020ed891599f Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 27 Mar 2019 22:50:52 -0400 Subject: [PATCH 202/605] Targeted ZHA permit joins. (#22482) * Targeted ZHA permit service. * Convert IEEE string to EUI64 usiv vol schema. * Update test units. * Lint. isort imports. --- homeassistant/components/zha/api.py | 64 ++++++++++++-------- homeassistant/components/zha/core/gateway.py | 39 ++++++------ homeassistant/components/zha/core/helpers.py | 2 + homeassistant/components/zha/services.yaml | 3 + tests/components/zha/test_binary_sensor.py | 7 +-- tests/components/zha/test_fan.py | 2 +- tests/components/zha/test_light.py | 4 +- tests/components/zha/test_sensor.py | 3 +- tests/components/zha/test_switch.py | 2 +- 9 files changed, 68 insertions(+), 58 deletions(-) diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 8bfcbc705dc7cb..2f88ad3a78bdec 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -7,6 +7,7 @@ import asyncio import logging + import voluptuous as vol from homeassistant.components import websocket_api @@ -14,12 +15,14 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import async_get_registry from homeassistant.helpers.dispatcher import async_dispatcher_connect + from .core.const import ( - DOMAIN, ATTR_CLUSTER_ID, ATTR_CLUSTER_TYPE, ATTR_ATTRIBUTE, ATTR_VALUE, - ATTR_MANUFACTURER, ATTR_COMMAND, ATTR_COMMAND_TYPE, ATTR_ARGS, IN, OUT, - CLIENT_COMMANDS, SERVER_COMMANDS, SERVER, NAME, ATTR_ENDPOINT_ID, - DATA_ZHA_GATEWAY, DATA_ZHA, MFG_CLUSTER_ID_START) -from .core.helpers import get_matched_clusters, async_is_bindable_target + ATTR_ARGS, ATTR_ATTRIBUTE, ATTR_CLUSTER_ID, ATTR_CLUSTER_TYPE, + ATTR_COMMAND, ATTR_COMMAND_TYPE, ATTR_ENDPOINT_ID, ATTR_MANUFACTURER, + ATTR_VALUE, CLIENT_COMMANDS, DATA_ZHA, DATA_ZHA_GATEWAY, DOMAIN, IN, + MFG_CLUSTER_ID_START, NAME, OUT, SERVER, SERVER_COMMANDS) +from .core.helpers import ( + async_is_bindable_target, convert_ieee, get_matched_clusters) _LOGGER = logging.getLogger(__name__) @@ -48,14 +51,15 @@ SERVICE_SCHEMAS = { SERVICE_PERMIT: vol.Schema({ + vol.Optional(ATTR_IEEE_ADDRESS, default=None): convert_ieee, vol.Optional(ATTR_DURATION, default=60): - vol.All(vol.Coerce(int), vol.Range(1, 254)), + vol.All(vol.Coerce(int), vol.Range(0, 254)), }), IEEE_SERVICE: vol.Schema({ - vol.Required(ATTR_IEEE_ADDRESS): cv.string, + vol.Required(ATTR_IEEE_ADDRESS): convert_ieee, }), SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE: vol.Schema({ - vol.Required(ATTR_IEEE): cv.string, + vol.Required(ATTR_IEEE): convert_ieee, vol.Required(ATTR_ENDPOINT_ID): cv.positive_int, vol.Required(ATTR_CLUSTER_ID): cv.positive_int, vol.Optional(ATTR_CLUSTER_TYPE, default=IN): cv.string, @@ -64,7 +68,7 @@ vol.Optional(ATTR_MANUFACTURER): cv.positive_int, }), SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND: vol.Schema({ - vol.Required(ATTR_IEEE): cv.string, + vol.Required(ATTR_IEEE): convert_ieee, vol.Required(ATTR_ENDPOINT_ID): cv.positive_int, vol.Required(ATTR_CLUSTER_ID): cv.positive_int, vol.Optional(ATTR_CLUSTER_TYPE, default=IN): cv.string, @@ -79,11 +83,16 @@ @websocket_api.require_admin @websocket_api.async_response @websocket_api.websocket_command({ - vol.Required('type'): 'zha/devices/permit' + vol.Required('type'): 'zha/devices/permit', + vol.Optional(ATTR_IEEE, default=None): convert_ieee, + vol.Optional(ATTR_DURATION, default=60): vol.All(vol.Coerce(int), + vol.Range(0, 254)) }) async def websocket_permit_devices(hass, connection, msg): """Permit ZHA zigbee devices.""" zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + duration = msg.get(ATTR_DURATION) + ieee = msg.get(ATTR_IEEE) async def forward_messages(data): """Forward events to websocket.""" @@ -103,8 +112,8 @@ def async_cleanup() -> None: connection.subscriptions[msg['id']] = async_cleanup zha_gateway.async_enable_debug_mode() - await zha_gateway.application_controller.permit(60) - + await zha_gateway.application_controller.permit(time_s=duration, + node=ieee) connection.send_result(msg['id']) @@ -153,7 +162,7 @@ def async_get_device_info(hass, device, ha_device_registry=None): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/reconfigure', - vol.Required(ATTR_IEEE): str + vol.Required(ATTR_IEEE): convert_ieee, }) async def websocket_reconfigure_node(hass, connection, msg): """Reconfigure a ZHA nodes entities by its ieee address.""" @@ -168,7 +177,7 @@ async def websocket_reconfigure_node(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters', - vol.Required(ATTR_IEEE): str + vol.Required(ATTR_IEEE): convert_ieee, }) async def websocket_device_clusters(hass, connection, msg): """Return a list of device clusters.""" @@ -201,7 +210,7 @@ async def websocket_device_clusters(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/attributes', - vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_IEEE): convert_ieee, vol.Required(ATTR_ENDPOINT_ID): int, vol.Required(ATTR_CLUSTER_ID): int, vol.Required(ATTR_CLUSTER_TYPE): str @@ -243,7 +252,7 @@ async def websocket_device_cluster_attributes(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/commands', - vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_IEEE): convert_ieee, vol.Required(ATTR_ENDPOINT_ID): int, vol.Required(ATTR_CLUSTER_ID): int, vol.Required(ATTR_CLUSTER_TYPE): str @@ -295,7 +304,7 @@ async def websocket_device_cluster_commands(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/clusters/attributes/value', - vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_IEEE): convert_ieee, vol.Required(ATTR_ENDPOINT_ID): int, vol.Required(ATTR_CLUSTER_ID): int, vol.Required(ATTR_CLUSTER_TYPE): str, @@ -340,7 +349,7 @@ async def websocket_read_zigbee_cluster_attributes(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/bindable', - vol.Required(ATTR_IEEE): str, + vol.Required(ATTR_IEEE): convert_ieee, }) async def websocket_get_bindable_devices(hass, connection, msg): """Directly bind devices.""" @@ -369,8 +378,8 @@ async def websocket_get_bindable_devices(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/bind', - vol.Required(ATTR_SOURCE_IEEE): str, - vol.Required(ATTR_TARGET_IEEE): str + vol.Required(ATTR_SOURCE_IEEE): convert_ieee, + vol.Required(ATTR_TARGET_IEEE): convert_ieee, }) async def websocket_bind_devices(hass, connection, msg): """Directly bind devices.""" @@ -389,8 +398,8 @@ async def websocket_bind_devices(hass, connection, msg): @websocket_api.async_response @websocket_api.websocket_command({ vol.Required(TYPE): 'zha/devices/unbind', - vol.Required(ATTR_SOURCE_IEEE): str, - vol.Required(ATTR_TARGET_IEEE): str + vol.Required(ATTR_SOURCE_IEEE): convert_ieee, + vol.Required(ATTR_TARGET_IEEE): convert_ieee, }) async def websocket_unbind_devices(hass, connection, msg): """Remove a direct binding between devices.""" @@ -450,17 +459,20 @@ def async_load_api(hass): async def permit(service): """Allow devices to join this network.""" duration = service.data.get(ATTR_DURATION) - _LOGGER.info("Permitting joins for %ss", duration) - await application_controller.permit(duration) + ieee = service.data.get(ATTR_IEEE_ADDRESS) + if ieee: + _LOGGER.info("Permitting joins for %ss on %s device", + duration, ieee) + else: + _LOGGER.info("Permitting joins for %ss", duration) + await application_controller.permit(time_s=duration, node=ieee) hass.helpers.service.async_register_admin_service( DOMAIN, SERVICE_PERMIT, permit, schema=SERVICE_SCHEMAS[SERVICE_PERMIT]) async def remove(service): """Remove a node from the network.""" - from bellows.types import EmberEUI64, uint8_t ieee = service.data.get(ATTR_IEEE_ADDRESS) - ieee = EmberEUI64([uint8_t(p, base=16) for p in ieee.split(':')]) _LOGGER.info("Removing node %s", ieee) await application_controller.remove(ieee) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 8ee2c7850e306c..4f1e24aad5b2e9 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -10,35 +10,31 @@ import itertools import logging import os - import traceback + from homeassistant.components.system_log import LogEntry, _figure_out_source from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_component import EntityComponent + +from ..api import async_get_device_info +from .channels import MAINS_POWERED, ZDOChannel from .const import ( - DATA_ZHA, DATA_ZHA_CORE_COMPONENT, DOMAIN, SIGNAL_REMOVE, DATA_ZHA_GATEWAY, - CONF_USB_PATH, CONF_BAUDRATE, DEFAULT_BAUDRATE, CONF_RADIO_TYPE, - DATA_ZHA_RADIO, CONF_DATABASE, DEFAULT_DATABASE_NAME, DATA_ZHA_BRIDGE_ID, - RADIO, CONTROLLER, RADIO_DESCRIPTION, BELLOWS, ZHA, ZIGPY, ZIGPY_XBEE, - ZIGPY_DECONZ, ORIGINAL, CURRENT, DEBUG_LEVELS, ADD_DEVICE_RELAY_LOGGERS, - TYPE, NWK, IEEE, MODEL, SIGNATURE, ATTR_MANUFACTURER, RAW_INIT, - ZHA_GW_MSG, DEVICE_REMOVED, DEVICE_INFO, DEVICE_FULL_INIT, DEVICE_JOINED, - LOG_OUTPUT, LOG_ENTRY -) -from .device import ZHADevice, DeviceStatus -from .channels import ( - ZDOChannel, MAINS_POWERED -) -from .helpers import convert_ieee + ADD_DEVICE_RELAY_LOGGERS, ATTR_MANUFACTURER, BELLOWS, CONF_BAUDRATE, + CONF_DATABASE, CONF_RADIO_TYPE, CONF_USB_PATH, CONTROLLER, CURRENT, + DATA_ZHA, DATA_ZHA_BRIDGE_ID, DATA_ZHA_CORE_COMPONENT, DATA_ZHA_GATEWAY, + DATA_ZHA_RADIO, DEBUG_LEVELS, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME, + DEVICE_FULL_INIT, DEVICE_INFO, DEVICE_JOINED, DEVICE_REMOVED, DOMAIN, IEEE, + LOG_ENTRY, LOG_OUTPUT, MODEL, NWK, ORIGINAL, RADIO, RADIO_DESCRIPTION, + RAW_INIT, SIGNAL_REMOVE, SIGNATURE, TYPE, ZHA, ZHA_GW_MSG, ZIGPY, + ZIGPY_DECONZ, ZIGPY_XBEE) +from .device import DeviceStatus, ZHADevice from .discovery import ( - async_process_endpoint, async_dispatch_discovery_info, - async_create_device_entity -) -from .store import async_get_registry + async_create_device_entity, async_dispatch_discovery_info, + async_process_endpoint) from .patches import apply_application_controller_patch from .registries import RADIO_TYPES -from ..api import async_get_device_info +from .store import async_get_registry _LOGGER = logging.getLogger(__name__) @@ -169,9 +165,8 @@ def device_removed(self, device): } ) - def get_device(self, ieee_str): + def get_device(self, ieee): """Return ZHADevice for given ieee.""" - ieee = convert_ieee(ieee_str) return self._devices.get(ieee) def get_entity_reference(self, entity_id): diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index b00626031eda0c..695f2be5960ad1 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -148,6 +148,8 @@ async def check_zigpy_connection(usb_path, radio_type, database_path): def convert_ieee(ieee_str): """Convert given ieee string to EUI64.""" from zigpy.types import EUI64, uint8_t + if ieee_str is None: + return None return EUI64([uint8_t(p, base=16) for p in ieee_str.split(':')]) diff --git a/homeassistant/components/zha/services.yaml b/homeassistant/components/zha/services.yaml index 0d7fe06fe25555..048054077f84db 100644 --- a/homeassistant/components/zha/services.yaml +++ b/homeassistant/components/zha/services.yaml @@ -6,6 +6,9 @@ permit: duration: description: Time to permit joins, in seconds example: 60 + ieee_address: + description: IEEE address of the node permitting new joins + example: "00:0d:6f:00:05:7d:2d:34" remove: description: Remove a node from the ZigBee network. diff --git a/tests/components/zha/test_binary_sensor.py b/tests/components/zha/test_binary_sensor.py index d0763b8fb10265..1d6b4fd3e01eb2 100644 --- a/tests/components/zha/test_binary_sensor.py +++ b/tests/components/zha/test_binary_sensor.py @@ -54,15 +54,14 @@ async def test_binary_sensor(hass, config_entry, zha_gateway): zone_cluster = zigpy_device_zone.endpoints.get( 1).ias_zone zone_entity_id = make_entity_id(DOMAIN, zigpy_device_zone, zone_cluster) - zone_zha_device = zha_gateway.get_device(str(zigpy_device_zone.ieee)) + zone_zha_device = zha_gateway.get_device(zigpy_device_zone.ieee) # occupancy binary_sensor occupancy_cluster = zigpy_device_occupancy.endpoints.get( 1).occupancy occupancy_entity_id = make_entity_id( DOMAIN, zigpy_device_occupancy, occupancy_cluster) - occupancy_zha_device = zha_gateway.get_device( - str(zigpy_device_occupancy.ieee)) + occupancy_zha_device = zha_gateway.get_device(zigpy_device_occupancy.ieee) # dimmable binary_sensor remote_on_off_cluster = zigpy_device_remote.endpoints.get( @@ -72,7 +71,7 @@ async def test_binary_sensor(hass, config_entry, zha_gateway): remote_entity_id = make_entity_id(DOMAIN, zigpy_device_remote, remote_on_off_cluster, use_suffix=False) - remote_zha_device = zha_gateway.get_device(str(zigpy_device_remote.ieee)) + remote_zha_device = zha_gateway.get_device(zigpy_device_remote.ieee) # test that the sensors exist and are in the unavailable state assert hass.states.get(zone_entity_id).state == STATE_UNAVAILABLE diff --git a/tests/components/zha/test_fan.py b/tests/components/zha/test_fan.py index a70e0e5ea40077..6f31f1bcad329f 100644 --- a/tests/components/zha/test_fan.py +++ b/tests/components/zha/test_fan.py @@ -31,7 +31,7 @@ async def test_fan(hass, config_entry, zha_gateway): cluster = zigpy_device.endpoints.get(1).fan entity_id = make_entity_id(DOMAIN, zigpy_device, cluster) - zha_device = zha_gateway.get_device(str(zigpy_device.ieee)) + zha_device = zha_gateway.get_device(zigpy_device.ieee) # test that the fan was created and that it is unavailable assert hass.states.get(entity_id).state == STATE_UNAVAILABLE diff --git a/tests/components/zha/test_light.py b/tests/components/zha/test_light.py index 0ccad52d6aa3e3..e9d6370575b7a8 100644 --- a/tests/components/zha/test_light.py +++ b/tests/components/zha/test_light.py @@ -51,7 +51,7 @@ async def test_light(hass, config_entry, zha_gateway, monkeypatch): on_off_entity_id = make_entity_id(DOMAIN, zigpy_device_on_off, on_off_device_on_off_cluster, use_suffix=False) - on_off_zha_device = zha_gateway.get_device(str(zigpy_device_on_off.ieee)) + on_off_zha_device = zha_gateway.get_device(zigpy_device_on_off.ieee) # dimmable light level_device_on_off_cluster = zigpy_device_level.endpoints.get(1).on_off @@ -65,7 +65,7 @@ async def test_light(hass, config_entry, zha_gateway, monkeypatch): level_entity_id = make_entity_id(DOMAIN, zigpy_device_level, level_device_on_off_cluster, use_suffix=False) - level_zha_device = zha_gateway.get_device(str(zigpy_device_level.ieee)) + level_zha_device = zha_gateway.get_device(zigpy_device_level.ieee) # test that the lights were created and that they are unavailable assert hass.states.get(on_off_entity_id).state == STATE_UNAVAILABLE diff --git a/tests/components/zha/test_sensor.py b/tests/components/zha/test_sensor.py index c348ef0d0a718e..ec6af7f4aa196d 100644 --- a/tests/components/zha/test_sensor.py +++ b/tests/components/zha/test_sensor.py @@ -114,8 +114,7 @@ async def async_build_devices(hass, zha_gateway, config_entry, cluster_ids): 1).in_clusters[cluster_id] device_info["entity_id"] = make_entity_id( DOMAIN, zigpy_device, device_info["cluster"]) - device_info["zha_device"] = zha_gateway.get_device( - str(zigpy_device.ieee)) + device_info["zha_device"] = zha_gateway.get_device(zigpy_device.ieee) return device_infos diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index 1fc21e34cd8329..b0bbc103a9e3a0 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -28,7 +28,7 @@ async def test_switch(hass, config_entry, zha_gateway): cluster = zigpy_device.endpoints.get(1).on_off entity_id = make_entity_id(DOMAIN, zigpy_device, cluster) - zha_device = zha_gateway.get_device(str(zigpy_device.ieee)) + zha_device = zha_gateway.get_device(zigpy_device.ieee) # test that the switch was created and that its state is unavailable assert hass.states.get(entity_id).state == STATE_UNAVAILABLE From 26d4736ebfe43bb7f9f3018edf939475babed62c Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Thu, 28 Mar 2019 03:51:22 +0100 Subject: [PATCH 203/605] Fix auto discovery of yeelights (#22481) * Fix auto discovery of yeelights * Fix lint --- homeassistant/components/yeelight/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 4171005d9fcc89..14b4656c403831 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -144,7 +144,7 @@ def _parse_custom_effects(effects_config): def setup(hass, config): """Set up the Yeelight bulbs.""" - conf = config[DOMAIN] + conf = config.get(DOMAIN, {}) yeelight_data = hass.data[DATA_YEELIGHT] = {} def device_discovered(service, info): @@ -169,12 +169,13 @@ def update(event): device.update() track_time_interval( - hass, update, conf[CONF_SCAN_INTERVAL] + hass, update, conf.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL) ) - for ipaddr, device_config in conf[CONF_DEVICES].items(): - _LOGGER.debug("Adding configured %s", device_config[CONF_NAME]) - _setup_device(hass, config, ipaddr, device_config) + if DOMAIN in config: + for ipaddr, device_config in conf[CONF_DEVICES].items(): + _LOGGER.debug("Adding configured %s", device_config[CONF_NAME]) + _setup_device(hass, config, ipaddr, device_config) return True @@ -192,7 +193,7 @@ def _setup_device(hass, hass_config, ipaddr, device_config): platform_config = device_config.copy() platform_config[CONF_HOST] = ipaddr platform_config[CONF_CUSTOM_EFFECTS] = _parse_custom_effects( - hass_config[DATA_YEELIGHT].get(CONF_CUSTOM_EFFECTS, {}) + hass_config.get(DOMAIN, {}).get(CONF_CUSTOM_EFFECTS, {}) ) load_platform(hass, LIGHT_DOMAIN, DOMAIN, platform_config, hass_config) From d13c892b281049415c67370d34ca711b5c3691c5 Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Wed, 27 Mar 2019 22:54:01 -0400 Subject: [PATCH 204/605] fix inverse state changes for binary sensors (#22479) --- homeassistant/components/konnected/__init__.py | 2 +- homeassistant/components/konnected/handlers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index 3a2dc3b2417ab0..276e395817c0d5 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -429,7 +429,7 @@ async def get(self, request: Request, device_id) -> Response: if not pin: return self.json_message( - 'Switch on pin ' + pin_num + ' not configured', + format('Switch on pin {} not configured', pin_num), status_code=HTTP_NOT_FOUND) return self.json( diff --git a/homeassistant/components/konnected/handlers.py b/homeassistant/components/konnected/handlers.py index 6e92e7f20c8615..bd93ee80e21cf5 100644 --- a/homeassistant/components/konnected/handlers.py +++ b/homeassistant/components/konnected/handlers.py @@ -19,7 +19,7 @@ async def async_handle_state_update(hass, context, msg): _LOGGER.debug("[state handler] context: %s msg: %s", context, msg) entity_id = context.get(ATTR_ENTITY_ID) state = bool(int(msg.get(ATTR_STATE))) - if msg.get(CONF_INVERSE): + if context.get(CONF_INVERSE): state = not state async_dispatcher_send( From 8f3434c2ab7140592de45454a7bf00526941c610 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 28 Mar 2019 03:54:27 +0100 Subject: [PATCH 205/605] Fix events so they work with multiple devices (#22477) --- homeassistant/components/axis/binary_sensor.py | 4 ++-- homeassistant/components/axis/device.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index 3a9583f64193c9..6d373dd638f67f 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -24,8 +24,8 @@ def async_add_sensor(event): """Add binary sensor from Axis device.""" async_add_entities([AxisBinarySensor(event, device)], True) - device.listeners.append( - async_dispatcher_connect(hass, 'axis_add_sensor', async_add_sensor)) + device.listeners.append(async_dispatcher_connect( + hass, device.event_new_sensor, async_add_sensor)) class AxisBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index 0d7d9348870be3..ffe48e5f733b1c 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -99,11 +99,16 @@ async def async_setup(self): return True + @property + def event_new_sensor(self): + """Device specific event to signal new sensor available.""" + return 'axis_add_sensor_{}'.format(self.serial) + @callback def async_signal_callback(self, action, event): """Call to configure events when initialized on event stream.""" if action == 'add': - async_dispatcher_send(self.hass, 'axis_add_sensor', event) + async_dispatcher_send(self.hass, self.event_new_sensor, event) @callback def shutdown(self, event): From c7904a4b378546556029e6c11d4f672f45b9b7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Thu, 28 Mar 2019 03:54:44 +0100 Subject: [PATCH 206/605] Improve Sensibo error handling (#22475) * Handle sensibo exception * improve sensibo --- homeassistant/components/sensibo/climate.py | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 7850b08fd6b786..3affaba3e1f83f 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -12,16 +12,15 @@ import async_timeout import voluptuous as vol -from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_STATE, ATTR_TEMPERATURE, CONF_API_KEY, CONF_ID, - STATE_ON, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA from homeassistant.components.climate.const import ( - ATTR_CURRENT_HUMIDITY, DOMAIN, - SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, + DOMAIN, SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE, SUPPORT_FAN_MODE, SUPPORT_SWING_MODE, SUPPORT_ON_OFF, STATE_HEAT, STATE_COOL, STATE_FAN_ONLY, STATE_DRY, STATE_AUTO) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_STATE, ATTR_TEMPERATURE, CONF_API_KEY, CONF_ID, + STATE_ON, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -86,7 +85,7 @@ async def async_setup_platform(hass, config, async_add_entities, devices.append(SensiboClimate( client, dev, hass.config.units.temperature_unit)) except (aiohttp.client_exceptions.ClientConnectorError, - asyncio.TimeoutError): + asyncio.TimeoutError, pysensibo.SensiboError): _LOGGER.exception('Failed to connect to Sensibo servers.') raise PlatformNotReady @@ -128,6 +127,7 @@ def __init__(self, client, data, units): self._id = data['id'] self._external_state = None self._units = units + self._available = False self._do_update(data) @property @@ -139,7 +139,7 @@ def _do_update(self, data): self._name = data['room']['name'] self._measurements = data['measurements'] self._ac_states = data['acState'] - self._status = data['connectionStatus']['isAlive'] + self._available = data['connectionStatus']['isAlive'] capabilities = data['remoteCapabilities'] self._operations = [SENSIBO_TO_HA[mode] for mode in capabilities['modes']] @@ -168,8 +168,7 @@ def state(self): @property def device_state_attributes(self): """Return the state attributes.""" - return {ATTR_CURRENT_HUMIDITY: self.current_humidity, - 'battery': self.current_battery} + return {'battery': self.current_battery} @property def temperature_unit(self): @@ -179,7 +178,7 @@ def temperature_unit(self): @property def available(self): """Return True if entity is available.""" - return self._status + return self._available @property def target_temperature(self): @@ -348,10 +347,13 @@ async def async_assume_state(self, state): async def async_update(self): """Retrieve latest state.""" + import pysensibo try: with async_timeout.timeout(TIMEOUT): data = await self._client.async_get_device( self._id, _FETCH_FIELDS) self._do_update(data) - except aiohttp.client_exceptions.ClientError: + except (aiohttp.client_exceptions.ClientError, + pysensibo.SensiboError): _LOGGER.warning('Failed to connect to Sensibo servers.') + self._available = False From b8e38c1b250cdf7c69f688aa7fe1c7d355da8a0c Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Thu, 28 Mar 2019 03:56:27 +0100 Subject: [PATCH 207/605] Add new data fields and bump python-join-api (#22472) * Add new data fields and bump python-join-api * Update __init__.py --- homeassistant/components/joaoapps_join/__init__.py | 2 +- homeassistant/components/joaoapps_join/notify.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/joaoapps_join/__init__.py b/homeassistant/components/joaoapps_join/__init__.py index adc856bdd3a242..f1371deed2bdf5 100644 --- a/homeassistant/components/joaoapps_join/__init__.py +++ b/homeassistant/components/joaoapps_join/__init__.py @@ -6,7 +6,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_NAME, CONF_API_KEY -REQUIREMENTS = ['python-join-api==0.0.2'] +REQUIREMENTS = ['python-join-api==0.0.4'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/joaoapps_join/notify.py b/homeassistant/components/joaoapps_join/notify.py index c586147d632d44..0137520049d423 100644 --- a/homeassistant/components/joaoapps_join/notify.py +++ b/homeassistant/components/joaoapps_join/notify.py @@ -7,7 +7,7 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-join-api==0.0.2'] +REQUIREMENTS = ['python-join-api==0.0.4'] _LOGGER = logging.getLogger(__name__) @@ -61,4 +61,7 @@ def send_message(self, message="", **kwargs): device_id=self._device_id, device_ids=self._device_ids, device_names=self._device_names, text=message, title=title, icon=data.get('icon'), smallicon=data.get('smallicon'), + image=data.get('image'), sound=data.get('sound'), + notification_id=data.get('notification_id'), url=data.get('url'), + tts=data.get('tts'), tts_language=data.get('tts_language'), vibration=data.get('vibration'), api_key=self._api_key) From e022f4465cda556d66023fc3b60a9b0e8a16ada1 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Thu, 28 Mar 2019 02:58:06 +0000 Subject: [PATCH 208/605] Bump pypi again for Cisco Mobility Express (#22467) * Fix empty device clId Fix for https://github.com/fbradyirl/ciscomobilityexpress/issues/13 * Update requirements_all.txt --- .../components/cisco_mobility_express/device_tracker.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cisco_mobility_express/device_tracker.py b/homeassistant/components/cisco_mobility_express/device_tracker.py index c5e2b4284d32a0..a722a994350041 100644 --- a/homeassistant/components/cisco_mobility_express/device_tracker.py +++ b/homeassistant/components/cisco_mobility_express/device_tracker.py @@ -10,7 +10,7 @@ CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_SSL, CONF_VERIFY_SSL) -REQUIREMENTS = ['ciscomobilityexpress==0.1.4'] +REQUIREMENTS = ['ciscomobilityexpress==0.1.5'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 2b993100f58a18..51aac4420b1358 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -268,7 +268,7 @@ buienradar==0.91 caldav==0.5.0 # homeassistant.components.cisco_mobility_express.device_tracker -ciscomobilityexpress==0.1.4 +ciscomobilityexpress==0.1.5 # homeassistant.components.notify.ciscospark ciscosparkapi==0.4.2 From 14ceb8472f7d24d60809909b54bd5fbc5ce3fc4b Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Thu, 28 Mar 2019 02:58:52 +0000 Subject: [PATCH 209/605] Return percentage information in Alexa Smart Home response (#22440) --- homeassistant/components/alexa/smart_home.py | 26 +++++++++ tests/components/alexa/test_smart_home.py | 57 ++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index c87b2c3f624afa..e16a1d45ab7fa7 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -65,6 +65,12 @@ (climate.STATE_DRY, 'OFF'), ]) +PERCENTAGE_FAN_MAP = { + fan.SPEED_LOW: 33, + fan.SPEED_MEDIUM: 66, + fan.SPEED_HIGH: 100, +} + SMART_HOME_HTTP_ENDPOINT = '/api/alexa/smart_home' CONF_DESCRIPTION = 'description' @@ -580,6 +586,26 @@ class _AlexaPercentageController(_AlexaInterface): def name(self): return 'Alexa.PercentageController' + def properties_supported(self): + return [{'name': 'percentage'}] + + def properties_retrievable(self): + return True + + def get_property(self, name): + if name != 'percentage': + raise _UnsupportedProperty(name) + + if self.entity.domain == fan.DOMAIN: + speed = self.entity.attributes.get(fan.ATTR_SPEED) + + return PERCENTAGE_FAN_MAP.get(speed, 0) + + if self.entity.domain == cover.DOMAIN: + return self.entity.attributes.get(cover.ATTR_CURRENT_POSITION, 0) + + return 0 + class _AlexaSpeaker(_AlexaInterface): """Implements Alexa.Speaker. diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index 845e59295ac65d..924a568dea2f79 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -1490,6 +1490,63 @@ async def test_report_colored_temp_light_state(hass): 'colorTemperatureInKelvin', 0) +async def test_report_fan_speed_state(hass): + """Test PercentageController reports fan speed correctly.""" + hass.states.async_set( + 'fan.off', 'off', {'friendly_name': "Off fan", + 'speed': "off", + 'supported_features': 1}) + hass.states.async_set( + 'fan.low_speed', 'on', {'friendly_name': "Low speed fan", + 'speed': "low", + 'supported_features': 1}) + hass.states.async_set( + 'fan.medium_speed', 'on', {'friendly_name': "Medium speed fan", + 'speed': "medium", + 'supported_features': 1}) + hass.states.async_set( + 'fan.high_speed', 'on', {'friendly_name': "High speed fan", + 'speed': "high", + 'supported_features': 1}) + + properties = await reported_properties(hass, 'fan.off') + properties.assert_equal('Alexa.PercentageController', 'percentage', 0) + + properties = await reported_properties(hass, 'fan.low_speed') + properties.assert_equal('Alexa.PercentageController', 'percentage', 33) + + properties = await reported_properties(hass, 'fan.medium_speed') + properties.assert_equal('Alexa.PercentageController', 'percentage', 66) + + properties = await reported_properties(hass, 'fan.high_speed') + properties.assert_equal('Alexa.PercentageController', 'percentage', 100) + + +async def test_report_cover_percentage_state(hass): + """Test PercentageController reports cover percentage correctly.""" + hass.states.async_set( + 'cover.fully_open', 'open', {'friendly_name': "Fully open cover", + 'current_position': 100, + 'supported_features': 15}) + hass.states.async_set( + 'cover.half_open', 'open', {'friendly_name': "Half open cover", + 'current_position': 50, + 'supported_features': 15}) + hass.states.async_set( + 'cover.closed', 'closed', {'friendly_name': "Closed cover", + 'current_position': 0, + 'supported_features': 15}) + + properties = await reported_properties(hass, 'cover.fully_open') + properties.assert_equal('Alexa.PercentageController', 'percentage', 100) + + properties = await reported_properties(hass, 'cover.half_open') + properties.assert_equal('Alexa.PercentageController', 'percentage', 50) + + properties = await reported_properties(hass, 'cover.closed') + properties.assert_equal('Alexa.PercentageController', 'percentage', 0) + + async def reported_properties(hass, endpoint): """Use ReportState to get properties and return them. From 8bf5e57b7fdacae3e0b31e16c38154ae2728d7b4 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Thu, 28 Mar 2019 03:01:10 +0000 Subject: [PATCH 210/605] Move HKDevice into connection (#22430) --- .../components/homekit_controller/__init__.py | 161 +----------------- .../homekit_controller/connection.py | 161 ++++++++++++++++++ .../components/homekit_controller/const.py | 3 + tests/components/homekit_controller/common.py | 5 +- 4 files changed, 170 insertions(+), 160 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index cf349854f52abd..c777d2944c19cd 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -1,24 +1,20 @@ """Support for Homekit device discovery.""" -import asyncio import logging import os from homeassistant.components.discovery import SERVICE_HOMEKIT from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import call_later from .config_flow import load_old_pairings -from .connection import get_accessory_information +from .connection import get_accessory_information HKDevice from .const import ( - CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_DEVICES + CONTROLLER, HOMEKIT_DIR, KNOWN_DEVICES, PAIRING_FILE ) - +from .const import DOMAIN # noqa: pylint: disable=unused-import REQUIREMENTS = ['homekit[IP]==0.13.0'] -HOMEKIT_DIR = '.homekit' - HOMEKIT_IGNORE = [ 'BSB002', 'Home Assistant Bridge', @@ -27,163 +23,12 @@ _LOGGER = logging.getLogger(__name__) -RETRY_INTERVAL = 60 # seconds - -PAIRING_FILE = "pairing.json" - def escape_characteristic_name(char_name): """Escape any dash or dots in a characteristics name.""" return char_name.replace('-', '_').replace('.', '_') -class HKDevice(): - """HomeKit device.""" - - def __init__(self, hass, host, port, model, hkid, config_num, config): - """Initialise a generic HomeKit device.""" - _LOGGER.info("Setting up Homekit device %s", model) - self.hass = hass - self.controller = hass.data[CONTROLLER] - - self.host = host - self.port = port - self.model = model - self.hkid = hkid - self.config_num = config_num - self.config = config - self.configurator = hass.components.configurator - self._connection_warning_logged = False - - # This just tracks aid/iid pairs so we know if a HK service has been - # mapped to a HA entity. - self.entities = [] - - self.pairing_lock = asyncio.Lock(loop=hass.loop) - - self.pairing = self.controller.pairings.get(hkid) - - if self.pairing is not None: - self.accessory_setup() - else: - self.configure() - - def accessory_setup(self): - """Handle setup of a HomeKit accessory.""" - # pylint: disable=import-error - from homekit.model.services import ServicesTypes - from homekit.exceptions import AccessoryDisconnectedError - - self.pairing.pairing_data['AccessoryIP'] = self.host - self.pairing.pairing_data['AccessoryPort'] = self.port - - try: - data = self.pairing.list_accessories_and_characteristics() - except AccessoryDisconnectedError: - call_later( - self.hass, RETRY_INTERVAL, lambda _: self.accessory_setup()) - return - for accessory in data: - aid = accessory['aid'] - for service in accessory['services']: - iid = service['iid'] - if (aid, iid) in self.entities: - # Don't add the same entity again - continue - - devtype = ServicesTypes.get_short(service['type']) - _LOGGER.debug("Found %s", devtype) - service_info = {'serial': self.hkid, - 'aid': aid, - 'iid': service['iid'], - 'model': self.model, - 'device-type': devtype} - component = HOMEKIT_ACCESSORY_DISPATCH.get(devtype, None) - if component is not None: - discovery.load_platform(self.hass, component, DOMAIN, - service_info, self.config) - self.entities.append((aid, iid)) - - def device_config_callback(self, callback_data): - """Handle initial pairing.""" - import homekit # pylint: disable=import-error - code = callback_data.get('code').strip() - try: - self.controller.perform_pairing(self.hkid, self.hkid, code) - except homekit.UnavailableError: - error_msg = "This accessory is already paired to another device. \ - Please reset the accessory and try again." - _configurator = self.hass.data[DOMAIN+self.hkid] - self.configurator.notify_errors(_configurator, error_msg) - return - except homekit.AuthenticationError: - error_msg = "Incorrect HomeKit code for {}. Please check it and \ - try again.".format(self.model) - _configurator = self.hass.data[DOMAIN+self.hkid] - self.configurator.notify_errors(_configurator, error_msg) - return - except homekit.UnknownError: - error_msg = "Received an unknown error. Please file a bug." - _configurator = self.hass.data[DOMAIN+self.hkid] - self.configurator.notify_errors(_configurator, error_msg) - raise - - self.pairing = self.controller.pairings.get(self.hkid) - if self.pairing is not None: - pairing_file = os.path.join( - self.hass.config.path(), - HOMEKIT_DIR, - PAIRING_FILE, - ) - self.controller.save_data(pairing_file) - _configurator = self.hass.data[DOMAIN+self.hkid] - self.configurator.request_done(_configurator) - self.accessory_setup() - else: - error_msg = "Unable to pair, please try again" - _configurator = self.hass.data[DOMAIN+self.hkid] - self.configurator.notify_errors(_configurator, error_msg) - - def configure(self): - """Obtain the pairing code for a HomeKit device.""" - description = "Please enter the HomeKit code for your {}".format( - self.model) - self.hass.data[DOMAIN+self.hkid] = \ - self.configurator.request_config(self.model, - self.device_config_callback, - description=description, - submit_caption="submit", - fields=[{'id': 'code', - 'name': 'HomeKit code', - 'type': 'string'}]) - - async def get_characteristics(self, *args, **kwargs): - """Read latest state from homekit accessory.""" - async with self.pairing_lock: - chars = await self.hass.async_add_executor_job( - self.pairing.get_characteristics, - *args, - **kwargs, - ) - return chars - - async def put_characteristics(self, characteristics): - """Control a HomeKit device state from Home Assistant.""" - chars = [] - for row in characteristics: - chars.append(( - row['aid'], - row['iid'], - row['value'], - )) - - async with self.pairing_lock: - await self.hass.async_add_executor_job( - self.pairing.put_characteristics, - chars - ) - - class HomeKitEntity(Entity): """Representation of a Home Assistant HomeKit device.""" diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 5550846120b2a6..d875b91eb2c54d 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -1,4 +1,19 @@ """Helpers for managing a pairing with a HomeKit accessory or bridge.""" +import asyncio +import logging +import os + +from homeassistant.helpers import discovery +from homeassistant.helpers.event import call_later + +from .const import ( + CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, PAIRING_FILE, HOMEKIT_DIR +) + + +RETRY_INTERVAL = 60 # seconds + +_LOGGER = logging.getLogger(__name__) def get_accessory_information(accessory): @@ -33,3 +48,149 @@ def get_accessory_name(accessory_info): if field in accessory_info: return accessory_info[field] return None + + +class HKDevice(): + """HomeKit device.""" + + def __init__(self, hass, host, port, model, hkid, config_num, config): + """Initialise a generic HomeKit device.""" + _LOGGER.info("Setting up Homekit device %s", model) + self.hass = hass + self.controller = hass.data[CONTROLLER] + + self.host = host + self.port = port + self.model = model + self.hkid = hkid + self.config_num = config_num + self.config = config + self.configurator = hass.components.configurator + self._connection_warning_logged = False + + # This just tracks aid/iid pairs so we know if a HK service has been + # mapped to a HA entity. + self.entities = [] + + self.pairing_lock = asyncio.Lock(loop=hass.loop) + + self.pairing = self.controller.pairings.get(hkid) + + if self.pairing is not None: + self.accessory_setup() + else: + self.configure() + + def accessory_setup(self): + """Handle setup of a HomeKit accessory.""" + # pylint: disable=import-error + from homekit.model.services import ServicesTypes + from homekit.exceptions import AccessoryDisconnectedError + + self.pairing.pairing_data['AccessoryIP'] = self.host + self.pairing.pairing_data['AccessoryPort'] = self.port + + try: + data = self.pairing.list_accessories_and_characteristics() + except AccessoryDisconnectedError: + call_later( + self.hass, RETRY_INTERVAL, lambda _: self.accessory_setup()) + return + for accessory in data: + aid = accessory['aid'] + for service in accessory['services']: + iid = service['iid'] + if (aid, iid) in self.entities: + # Don't add the same entity again + continue + + devtype = ServicesTypes.get_short(service['type']) + _LOGGER.debug("Found %s", devtype) + service_info = {'serial': self.hkid, + 'aid': aid, + 'iid': service['iid'], + 'model': self.model, + 'device-type': devtype} + component = HOMEKIT_ACCESSORY_DISPATCH.get(devtype, None) + if component is not None: + discovery.load_platform(self.hass, component, DOMAIN, + service_info, self.config) + + def device_config_callback(self, callback_data): + """Handle initial pairing.""" + import homekit # pylint: disable=import-error + code = callback_data.get('code').strip() + try: + self.controller.perform_pairing(self.hkid, self.hkid, code) + except homekit.UnavailableError: + error_msg = "This accessory is already paired to another device. \ + Please reset the accessory and try again." + _configurator = self.hass.data[DOMAIN+self.hkid] + self.configurator.notify_errors(_configurator, error_msg) + return + except homekit.AuthenticationError: + error_msg = "Incorrect HomeKit code for {}. Please check it and \ + try again.".format(self.model) + _configurator = self.hass.data[DOMAIN+self.hkid] + self.configurator.notify_errors(_configurator, error_msg) + return + except homekit.UnknownError: + error_msg = "Received an unknown error. Please file a bug." + _configurator = self.hass.data[DOMAIN+self.hkid] + self.configurator.notify_errors(_configurator, error_msg) + raise + + self.pairing = self.controller.pairings.get(self.hkid) + if self.pairing is not None: + pairing_file = os.path.join( + self.hass.config.path(), + HOMEKIT_DIR, + PAIRING_FILE, + ) + self.controller.save_data(pairing_file) + _configurator = self.hass.data[DOMAIN+self.hkid] + self.configurator.request_done(_configurator) + self.accessory_setup() + else: + error_msg = "Unable to pair, please try again" + _configurator = self.hass.data[DOMAIN+self.hkid] + self.configurator.notify_errors(_configurator, error_msg) + + def configure(self): + """Obtain the pairing code for a HomeKit device.""" + description = "Please enter the HomeKit code for your {}".format( + self.model) + self.hass.data[DOMAIN+self.hkid] = \ + self.configurator.request_config(self.model, + self.device_config_callback, + description=description, + submit_caption="submit", + fields=[{'id': 'code', + 'name': 'HomeKit code', + 'type': 'string'}]) + + async def get_characteristics(self, *args, **kwargs): + """Read latest state from homekit accessory.""" + async with self.pairing_lock: + chars = await self.hass.async_add_executor_job( + self.pairing.get_characteristics, + *args, + **kwargs, + ) + return chars + + async def put_characteristics(self, characteristics): + """Control a HomeKit device state from Home Assistant.""" + chars = [] + for row in characteristics: + chars.append(( + row['aid'], + row['iid'], + row['value'], + )) + + async with self.pairing_lock: + await self.hass.async_add_executor_job( + self.pairing.put_characteristics, + chars + ) diff --git a/homeassistant/components/homekit_controller/const.py b/homeassistant/components/homekit_controller/const.py index 90a105b0ad9c41..de9663f1202522 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -4,6 +4,9 @@ KNOWN_DEVICES = "{}-devices".format(DOMAIN) CONTROLLER = "{}-controller".format(DOMAIN) +HOMEKIT_DIR = '.homekit' +PAIRING_FILE = 'pairing.json' + # Mapping from Homekit type to component. HOMEKIT_ACCESSORY_DISPATCH = { 'lightbulb': 'light', diff --git a/tests/components/homekit_controller/common.py b/tests/components/homekit_controller/common.py index 2c60dc168d015b..2d659d42dfb67a 100644 --- a/tests/components/homekit_controller/common.py +++ b/tests/components/homekit_controller/common.py @@ -8,8 +8,9 @@ AbstractCharacteristic, CharacteristicPermissions, CharacteristicsTypes) from homekit.model import Accessory, get_id from homekit.exceptions import AccessoryNotFoundError -from homeassistant.components.homekit_controller import ( - DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, SERVICE_HOMEKIT) +from homeassistant.components.homekit_controller import SERVICE_HOMEKIT +from homeassistant.components.homekit_controller.const import ( + DOMAIN, HOMEKIT_ACCESSORY_DISPATCH) from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from tests.common import async_fire_time_changed, fire_service_discovered From a1369c2fee4c6144a9f81951a9bdd674b9fda323 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Thu, 28 Mar 2019 04:02:04 +0100 Subject: [PATCH 211/605] Incoming SMS handling for netgear_lte (#22402) * Fire netgear_lte events for incoming SMS * Add netgear_lte.delete_sms service call * Fix log statement * Add services.yaml --- .../components/netgear_lte/__init__.py | 45 +++++++++++++++++++ .../components/netgear_lte/services.yaml | 9 ++++ 2 files changed, 54 insertions(+) create mode 100644 homeassistant/components/netgear_lte/services.yaml diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index 18a9e5e7c9def6..a259a361be42a7 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -30,6 +30,15 @@ DOMAIN = 'netgear_lte' DATA_KEY = 'netgear_lte' +EVENT_SMS = 'netgear_lte_sms' + +SERVICE_DELETE_SMS = 'delete_sms' + +ATTR_HOST = 'host' +ATTR_SMS_ID = 'sms_id' +ATTR_FROM = 'from' +ATTR_MESSAGE = 'message' + NOTIFY_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME, default=DOMAIN): cv.string, @@ -53,6 +62,11 @@ })]) }, extra=vol.ALLOW_EXTRA) +DELETE_SMS_SCHEMA = vol.Schema({ + vol.Required(ATTR_HOST): cv.string, + vol.Required(ATTR_SMS_ID): vol.All(cv.ensure_list, [cv.positive_int]), +}) + @attr.s class ModemData: @@ -101,6 +115,24 @@ async def async_setup(hass, config): hass, cookie_jar=aiohttp.CookieJar(unsafe=True)) hass.data[DATA_KEY] = LTEData(websession) + async def delete_sms_handler(service): + """Apply a service.""" + host = service.data[ATTR_HOST] + conf = {CONF_HOST: host} + modem_data = hass.data[DATA_KEY].get_modem_data(conf) + + if not modem_data: + _LOGGER.error( + "%s: host %s unavailable", SERVICE_DELETE_SMS, host) + return + + for sms_id in service.data[ATTR_SMS_ID]: + await modem_data.modem.delete_sms(sms_id) + + hass.services.async_register( + DOMAIN, SERVICE_DELETE_SMS, delete_sms_handler, + schema=DELETE_SMS_SCHEMA) + netgear_lte_config = config[DOMAIN] # Set up each modem @@ -161,6 +193,19 @@ def cleanup_retry(event): async def _login(hass, modem_data, password): """Log in and complete setup.""" await modem_data.modem.login(password=password) + + def fire_sms_event(sms): + """Send an SMS event.""" + data = { + ATTR_HOST: modem_data.host, + ATTR_SMS_ID: sms.id, + ATTR_FROM: sms.sender, + ATTR_MESSAGE: sms.message, + } + hass.bus.async_fire(EVENT_SMS, data) + + await modem_data.modem.add_sms_listener(fire_sms_event) + await modem_data.async_update() hass.data[DATA_KEY].modem_data[modem_data.host] = modem_data diff --git a/homeassistant/components/netgear_lte/services.yaml b/homeassistant/components/netgear_lte/services.yaml new file mode 100644 index 00000000000000..8f61e7a44b5e74 --- /dev/null +++ b/homeassistant/components/netgear_lte/services.yaml @@ -0,0 +1,9 @@ +delete_sms: + description: Delete messages from the modem inbox. + fields: + host: + description: The modem that should have a message deleted. + example: 192.168.5.1 + sms_id: + description: Integer or list of integers with inbox IDs of messages to delete. + example: 7 From f11f5255ae03b62848477c837b294d9810042bc4 Mon Sep 17 00:00:00 2001 From: Heine Furubotten Date: Thu, 28 Mar 2019 04:04:35 +0100 Subject: [PATCH 212/605] Entur upgrade to v0.2.0: async polling, number of departures, omit non boarding departures (#22001) * The Great Migration * removed outdated tests * Upgraded enturclient to v0.2.0 - client has gone async - configurable if non boarding departures should be omitted from result - configurable how many departures should be shown - more clear which attributes are included and when * reduced nesting and clearing of attributes * Removed test data * ensure attribution and stop id in attributes * docstring fixes * fix rebase errors went a bit too fast on continue on one of the conflicts.. * fix test requirements comment from gen --- .coveragerc | 1 + .../entur_public_transport/__init__.py | 2 +- .../entur_public_transport/sensor.py | 168 +++++++++++------- requirements_all.txt | 2 +- requirements_test_all.txt | 3 - script/gen_requirements_all.py | 1 - .../entur_public_transport/__init__.py | 1 - .../entur_public_transport/test_sensor.py | 66 ------- tests/fixtures/entur_public_transport.json | 111 ------------ 9 files changed, 106 insertions(+), 249 deletions(-) delete mode 100644 tests/components/entur_public_transport/__init__.py delete mode 100644 tests/components/entur_public_transport/test_sensor.py delete mode 100644 tests/fixtures/entur_public_transport.json diff --git a/.coveragerc b/.coveragerc index ec6aad90628d2a..84aed9dfb141b0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -160,6 +160,7 @@ omit = homeassistant/components/emulated_hue/upnp.py homeassistant/components/enigma2/media_player.py homeassistant/components/enocean/* + homeassistant/components/entur_public_transport/* homeassistant/components/envisalink/* homeassistant/components/esphome/__init__.py homeassistant/components/esphome/binary_sensor.py diff --git a/homeassistant/components/entur_public_transport/__init__.py b/homeassistant/components/entur_public_transport/__init__.py index 7d8e0a18d0bced..0bdce1909f4b71 100644 --- a/homeassistant/components/entur_public_transport/__init__.py +++ b/homeassistant/components/entur_public_transport/__init__.py @@ -1 +1 @@ -"""The entur_public_transport component.""" +"""Component for integrating entur public transport.""" diff --git a/homeassistant/components/entur_public_transport/sensor.py b/homeassistant/components/entur_public_transport/sensor.py index 330f5f8cc56ca0..b2e228676902f3 100644 --- a/homeassistant/components/entur_public_transport/sensor.py +++ b/homeassistant/components/entur_public_transport/sensor.py @@ -1,9 +1,4 @@ -""" -Real-time information about public transport departures in Norway. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.entur_public_transport/ -""" +"""Real-time information about public transport departures in Norway.""" from datetime import datetime, timedelta import logging @@ -13,17 +8,16 @@ from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_SHOW_ON_MAP) +from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle import homeassistant.util.dt as dt_util -REQUIREMENTS = ['enturclient==0.1.3'] +REQUIREMENTS = ['enturclient==0.2.0'] _LOGGER = logging.getLogger(__name__) -ATTR_NEXT_UP_IN = 'next_due_in' - API_CLIENT_NAME = 'homeassistant-homeassistant' ATTRIBUTION = "Data provided by entur.org under NLOD" @@ -31,6 +25,8 @@ CONF_STOP_IDS = 'stop_ids' CONF_EXPAND_PLATFORMS = 'expand_platforms' CONF_WHITELIST_LINES = 'line_whitelist' +CONF_OMIT_NON_BOARDING = 'omit_non_boarding' +CONF_NUMBER_OF_DEPARTURES = 'number_of_departures' DEFAULT_NAME = 'Entur' DEFAULT_ICON_KEY = 'bus' @@ -44,7 +40,7 @@ 'water': 'mdi:ferry', } -SCAN_INTERVAL = timedelta(minutes=1) +SCAN_INTERVAL = timedelta(seconds=45) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_STOP_IDS): vol.All(cv.ensure_list, [cv.string]), @@ -52,58 +48,80 @@ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_SHOW_ON_MAP, default=False): cv.boolean, vol.Optional(CONF_WHITELIST_LINES, default=[]): cv.ensure_list, + vol.Optional(CONF_OMIT_NON_BOARDING, default=True): cv.boolean, + vol.Optional(CONF_NUMBER_OF_DEPARTURES, default=2): + vol.All(cv.positive_int, vol.Range(min=2, max=10)) }) -def due_in_minutes(timestamp: str) -> str: - """Get the time in minutes from a timestamp. +ATTR_STOP_ID = 'stop_id' - The timestamp should be in the format - year-month-yearThour:minute:second+timezone - """ +ATTR_ROUTE = 'route' +ATTR_ROUTE_ID = 'route_id' +ATTR_EXPECTED_AT = 'due_at' +ATTR_DELAY = 'delay' +ATTR_REALTIME = 'real_time' + +ATTR_NEXT_UP_IN = 'next_due_in' +ATTR_NEXT_UP_ROUTE = 'next_route' +ATTR_NEXT_UP_ROUTE_ID = 'next_route_id' +ATTR_NEXT_UP_AT = 'next_due_at' +ATTR_NEXT_UP_DELAY = 'next_delay' +ATTR_NEXT_UP_REALTIME = 'next_real_time' + +ATTR_TRANSPORT_MODE = 'transport_mode' + + +def due_in_minutes(timestamp: datetime) -> int: + """Get the time in minutes from a timestamp.""" if timestamp is None: return None - diff = datetime.strptime( - timestamp, "%Y-%m-%dT%H:%M:%S%z") - dt_util.now() - - return str(int(diff.total_seconds() / 60)) + diff = timestamp - dt_util.now() + return int(diff.total_seconds() / 60) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Set up the Entur public transport sensor.""" from enturclient import EnturPublicTransportData - from enturclient.consts import CONF_NAME as API_NAME expand = config.get(CONF_EXPAND_PLATFORMS) line_whitelist = config.get(CONF_WHITELIST_LINES) name = config.get(CONF_NAME) show_on_map = config.get(CONF_SHOW_ON_MAP) stop_ids = config.get(CONF_STOP_IDS) + omit_non_boarding = config.get(CONF_OMIT_NON_BOARDING) + number_of_departures = config.get(CONF_NUMBER_OF_DEPARTURES) stops = [s for s in stop_ids if "StopPlace" in s] quays = [s for s in stop_ids if "Quay" in s] data = EnturPublicTransportData(API_CLIENT_NAME, - stops, - quays, - expand, - line_whitelist) - data.update() + stops=stops, + quays=quays, + line_whitelist=line_whitelist, + omit_non_boarding=omit_non_boarding, + number_of_departures=number_of_departures, + web_session=async_get_clientsession(hass)) + + if expand: + await data.expand_all_quays() + await data.update() proxy = EnturProxy(data) entities = [] - for item in data.all_stop_places_quays(): + for place in data.all_stop_places_quays(): try: given_name = "{} {}".format( - name, data.get_stop_info(item)[API_NAME]) + name, data.get_stop_info(place).name) except KeyError: - given_name = "{} {}".format(name, item) + given_name = "{} {}".format(name, place) entities.append( - EnturPublicTransportSensor(proxy, given_name, item, show_on_map)) + EnturPublicTransportSensor(proxy, given_name, place, show_on_map)) - add_entities(entities, True) + async_add_entities(entities, True) class EnturProxy: @@ -116,10 +134,10 @@ def __init__(self, api): """Initialize the proxy.""" self._api = api - @Throttle(SCAN_INTERVAL) - def update(self) -> None: + @Throttle(timedelta(seconds=15)) + async def async_update(self) -> None: """Update data in client.""" - self._api.update() + await self._api.update() def get_stop_info(self, stop_id: str) -> dict: """Get info about specific stop place.""" @@ -132,18 +150,13 @@ class EnturPublicTransportSensor(Entity): def __init__( self, api: EnturProxy, name: str, stop: str, show_on_map: bool): """Initialize the sensor.""" - from enturclient.consts import ATTR_STOP_ID - self.api = api self._stop = stop self._show_on_map = show_on_map self._name = name self._state = None self._icon = ICONS[DEFAULT_ICON_KEY] - self._attributes = { - ATTR_ATTRIBUTION: ATTRIBUTION, - ATTR_STOP_ID: self._stop, - } + self._attributes = {} @property def name(self) -> str: @@ -158,6 +171,8 @@ def state(self) -> str: @property def device_state_attributes(self) -> dict: """Return the state attributes.""" + self._attributes[ATTR_ATTRIBUTION] = ATTRIBUTION + self._attributes[ATTR_STOP_ID] = self._stop return self._attributes @property @@ -170,33 +185,56 @@ def icon(self) -> str: """Icon to use in the frontend.""" return self._icon - def update(self) -> None: + async def async_update(self) -> None: """Get the latest data and update the states.""" - from enturclient.consts import ( - ATTR, ATTR_EXPECTED_AT, ATTR_NEXT_UP_AT, CONF_LOCATION, - CONF_LATITUDE as LAT, CONF_LONGITUDE as LONG, CONF_TRANSPORT_MODE) + await self.api.async_update() - self.api.update() + self._attributes = {} data = self.api.get_stop_info(self._stop) - if data is not None and ATTR in data: - attrs = data[ATTR] - self._attributes.update(attrs) - - if ATTR_NEXT_UP_AT in attrs: - self._attributes[ATTR_NEXT_UP_IN] = \ - due_in_minutes(attrs[ATTR_NEXT_UP_AT]) - - if CONF_LOCATION in data and self._show_on_map: - self._attributes[CONF_LATITUDE] = data[CONF_LOCATION][LAT] - self._attributes[CONF_LONGITUDE] = data[CONF_LOCATION][LONG] - - if ATTR_EXPECTED_AT in attrs: - self._state = due_in_minutes(attrs[ATTR_EXPECTED_AT]) - else: - self._state = None - - self._icon = ICONS.get( - data[CONF_TRANSPORT_MODE], ICONS[DEFAULT_ICON_KEY]) - else: + if data is None: + self._state = None + return + + if self._show_on_map and data.latitude and data.longitude: + self._attributes[CONF_LATITUDE] = data.latitude + self._attributes[CONF_LONGITUDE] = data.longitude + + calls = data.estimated_calls + if not calls: self._state = None + return + + self._state = due_in_minutes(calls[0].expected_departure_time) + self._icon = ICONS.get( + calls[0].transport_mode, ICONS[DEFAULT_ICON_KEY]) + + self._attributes[ATTR_ROUTE] = calls[0].front_display + self._attributes[ATTR_ROUTE_ID] = calls[0].line_id + self._attributes[ATTR_EXPECTED_AT] = calls[0]\ + .expected_departure_time.strftime("%H:%M") + self._attributes[ATTR_REALTIME] = calls[0].is_realtime + self._attributes[ATTR_DELAY] = calls[0].delay_in_min + + number_of_calls = len(calls) + if number_of_calls < 2: + return + + self._attributes[ATTR_NEXT_UP_ROUTE] = calls[1].front_display + self._attributes[ATTR_NEXT_UP_ROUTE_ID] = calls[1].line_id + self._attributes[ATTR_NEXT_UP_AT] = calls[1]\ + .expected_departure_time.strftime("%H:%M") + self._attributes[ATTR_NEXT_UP_IN] = "{} min"\ + .format(due_in_minutes(calls[1].expected_departure_time)) + self._attributes[ATTR_NEXT_UP_REALTIME] = calls[1].is_realtime + self._attributes[ATTR_NEXT_UP_DELAY] = calls[1].delay_in_min + + if number_of_calls < 3: + return + + for i, call in enumerate(calls[2:]): + key_name = "departure_#" + str(i + 3) + self._attributes[key_name] = "{}{} {}".format( + "" if bool(call.is_realtime) else "ca. ", + call.expected_departure_time.strftime("%H:%M"), + call.front_display) diff --git a/requirements_all.txt b/requirements_all.txt index 51aac4420b1358..9dfac8a213014e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -388,7 +388,7 @@ emulated_roku==0.1.8 enocean==0.40 # homeassistant.components.entur_public_transport.sensor -enturclient==0.1.3 +enturclient==0.2.0 # homeassistant.components.envirophat.sensor # envirophat==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 08466d922b86c6..7721c236d995e2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -86,9 +86,6 @@ eebrightbox==0.0.4 # homeassistant.components.emulated_roku emulated_roku==0.1.8 -# homeassistant.components.entur_public_transport.sensor -enturclient==0.1.3 - # homeassistant.components.season.sensor ephem==3.7.6.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 5d21681aedace1..d2d6588672ff13 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -54,7 +54,6 @@ 'dsmr_parser', 'eebrightbox', 'emulated_roku', - 'enturclient', 'ephem', 'evohomeclient', 'feedparser-homeassistant', diff --git a/tests/components/entur_public_transport/__init__.py b/tests/components/entur_public_transport/__init__.py deleted file mode 100644 index 015a5e9103c9fd..00000000000000 --- a/tests/components/entur_public_transport/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the entur_public_transport component.""" diff --git a/tests/components/entur_public_transport/test_sensor.py b/tests/components/entur_public_transport/test_sensor.py deleted file mode 100644 index 98e72d19e8143b..00000000000000 --- a/tests/components/entur_public_transport/test_sensor.py +++ /dev/null @@ -1,66 +0,0 @@ -"""The tests for the entur platform.""" -from datetime import datetime -import unittest -from unittest.mock import patch - -from enturclient.api import RESOURCE -from enturclient.consts import ATTR_EXPECTED_AT, ATTR_ROUTE, ATTR_STOP_ID -import requests_mock - -from homeassistant.components.entur_public_transport.sensor import ( - CONF_EXPAND_PLATFORMS, CONF_STOP_IDS) -from homeassistant.setup import setup_component -import homeassistant.util.dt as dt_util - -from tests.common import get_test_home_assistant, load_fixture - -VALID_CONFIG = { - 'platform': 'entur_public_transport', - CONF_EXPAND_PLATFORMS: False, - CONF_STOP_IDS: [ - 'NSR:StopPlace:548', - 'NSR:Quay:48550', - ] -} - -FIXTURE_FILE = 'entur_public_transport.json' -TEST_TIMESTAMP = datetime(2018, 10, 10, 7, tzinfo=dt_util.UTC) - - -class TestEnturPublicTransportSensor(unittest.TestCase): - """Test the entur platform.""" - - def setUp(self): - """Initialize values for this testcase class.""" - self.hass = get_test_home_assistant() - - def tearDown(self): - """Stop everything that was started.""" - self.hass.stop() - - @requests_mock.Mocker() - @patch( - 'homeassistant.components.entur_public_transport.sensor.dt_util.now', - return_value=TEST_TIMESTAMP) - def test_setup(self, mock_req, mock_patch): - """Test for correct sensor setup with state and proper attributes.""" - mock_req.post(RESOURCE, - text=load_fixture(FIXTURE_FILE), - status_code=200) - self.assertTrue( - setup_component(self.hass, 'sensor', {'sensor': VALID_CONFIG})) - - state = self.hass.states.get('sensor.entur_bergen_stasjon') - assert state.state == '28' - assert state.attributes.get(ATTR_STOP_ID) == 'NSR:StopPlace:548' - assert state.attributes.get(ATTR_ROUTE) == "59 Bergen" - assert state.attributes.get(ATTR_EXPECTED_AT) \ - == '2018-10-10T09:28:00+0200' - - state = self.hass.states.get('sensor.entur_fiskepiren_platform_2') - assert state.state == '0' - assert state.attributes.get(ATTR_STOP_ID) == 'NSR:Quay:48550' - assert state.attributes.get(ATTR_ROUTE) \ - == "5 Stavanger Airport via Forum" - assert state.attributes.get(ATTR_EXPECTED_AT) \ - == '2018-10-10T09:00:00+0200' diff --git a/tests/fixtures/entur_public_transport.json b/tests/fixtures/entur_public_transport.json deleted file mode 100644 index 24eafe94b23ee1..00000000000000 --- a/tests/fixtures/entur_public_transport.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "data": { - "stopPlaces": [ - { - "id": "NSR:StopPlace:548", - "name": "Bergen stasjon", - "estimatedCalls": [ - { - "realtime": false, - "aimedArrivalTime": "2018-10-10T09:28:00+0200", - "aimedDepartureTime": "2018-10-10T09:28:00+0200", - "expectedArrivalTime": "2018-10-10T09:28:00+0200", - "expectedDepartureTime": "2018-10-10T09:28:00+0200", - "requestStop": false, - "notices": [], - "destinationDisplay": { - "frontText": "Bergen" - }, - "serviceJourney": { - "journeyPattern": { - "line": { - "id": "NSB:Line:45", - "name": "Vossabanen", - "transportMode": "rail", - "publicCode": "59" - } - } - } - }, - { - "realtime": false, - "aimedArrivalTime": "2018-10-10T09:35:00+0200", - "aimedDepartureTime": "2018-10-10T09:35:00+0200", - "expectedArrivalTime": "2018-10-10T09:35:00+0200", - "expectedDepartureTime": "2018-10-10T09:35:00+0200", - "requestStop": false, - "notices": [], - "destinationDisplay": { - "frontText": "Arna" - }, - "serviceJourney": { - "journeyPattern": { - "line": { - "id": "NSB:Line:45", - "name": "Vossabanen", - "transportMode": "rail", - "publicCode": "58" - } - } - } - } - ] - } - ], - "quays": [ - { - "id": "NSR:Quay:48550", - "name": "Fiskepiren", - "publicCode": "2", - "latitude": 59.960904, - "longitude": 10.882942, - "estimatedCalls": [ - { - "realtime": false, - "aimedArrivalTime": "2018-10-10T09:00:00+0200", - "aimedDepartureTime": "2018-10-10T09:00:00+0200", - "expectedArrivalTime": "2018-10-10T09:00:00+0200", - "expectedDepartureTime": "2018-10-10T09:00:00+0200", - "requestStop": false, - "notices": [], - "destinationDisplay": { - "frontText": "Stavanger Airport via Forum" - }, - "serviceJourney": { - "journeyPattern": { - "line": { - "id": "KOL:Line:2900_234", - "name": "Flybussen", - "transportMode": "bus", - "publicCode": "5" - } - } - } - }, - { - "realtime": false, - "aimedArrivalTime": "2018-10-10T09:06:00+0200", - "aimedDepartureTime": "2018-10-10T09:06:00+0200", - "expectedArrivalTime": "2018-10-10T09:06:00+0200", - "expectedDepartureTime": "2018-10-10T09:06:00+0200", - "requestStop": false, - "notices": [], - "destinationDisplay": { - "frontText": "Stavanger" - }, - "serviceJourney": { - "journeyPattern": { - "line": { - "id": "KOL:Line:1000_234", - "name": "1", - "transportMode": "bus", - "publicCode": "1" - } - } - } - } - ] - } - ] - } -} \ No newline at end of file From c4eab21736faeef3f51caa22a993cd3cc7b616d9 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 27 Mar 2019 20:09:36 -0700 Subject: [PATCH 213/605] Mopar split (#21526) * Split out mopar into a component and sensor platform * Add the mopar switch platform * Add the mopar lock platform * Clean up and bump version * Update per review * Re-add service to trigger horn * Clean up again * Don't call async from sync context * Lint * Implement changes from review * Lint * A little more clean up --- .coveragerc | 3 +- homeassistant/components/mopar/__init__.py | 158 ++++++++++++++++++- homeassistant/components/mopar/lock.py | 55 +++++++ homeassistant/components/mopar/sensor.py | 155 +++++------------- homeassistant/components/mopar/services.yaml | 6 + homeassistant/components/mopar/switch.py | 53 +++++++ requirements_all.txt | 2 +- 7 files changed, 317 insertions(+), 115 deletions(-) create mode 100644 homeassistant/components/mopar/lock.py create mode 100644 homeassistant/components/mopar/services.yaml create mode 100644 homeassistant/components/mopar/switch.py diff --git a/.coveragerc b/.coveragerc index 84aed9dfb141b0..2cfac103f3ca32 100644 --- a/.coveragerc +++ b/.coveragerc @@ -328,6 +328,7 @@ omit = homeassistant/components/mobile_app/* homeassistant/components/mochad/* homeassistant/components/modbus/* + homeassistant/components/mopar/* homeassistant/components/mychevy/* homeassistant/components/mycroft/* homeassistant/components/mysensors/* @@ -499,7 +500,7 @@ omit = homeassistant/components/miflora/sensor.py homeassistant/components/mitemp_bt/sensor.py homeassistant/components/modem_callerid/sensor.py - homeassistant/components/mopar/sensor.py + homeassistant/components/mopar/* homeassistant/components/mqtt_room/sensor.py homeassistant/components/mvglive/sensor.py homeassistant/components/nederlandse_spoorwegen/sensor.py diff --git a/homeassistant/components/mopar/__init__.py b/homeassistant/components/mopar/__init__.py index f13076fb763ff7..d845d585765ef2 100644 --- a/homeassistant/components/mopar/__init__.py +++ b/homeassistant/components/mopar/__init__.py @@ -1 +1,157 @@ -"""The mopar component.""" +"""Support for Mopar vehicles.""" +import logging +from datetime import timedelta + +import voluptuous as vol + +from homeassistant.components.lock import DOMAIN as LOCK +from homeassistant.components.sensor import DOMAIN as SENSOR +from homeassistant.components.switch import DOMAIN as SWITCH +from homeassistant.const import ( + CONF_USERNAME, + CONF_PASSWORD, + CONF_PIN, + CONF_SCAN_INTERVAL +) +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.discovery import load_platform +from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import track_time_interval + +REQUIREMENTS = ['motorparts==1.1.0'] + +DOMAIN = 'mopar' +DATA_UPDATED = '{}_data_updated'.format(DOMAIN) + +_LOGGER = logging.getLogger(__name__) + +COOKIE_FILE = 'mopar_cookies.pickle' +SUCCESS_RESPONSE = 'completed' + +SUPPORTED_PLATFORMS = [LOCK, SENSOR, SWITCH] + +DEFAULT_INTERVAL = timedelta(days=7) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_PIN): cv.positive_int, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): + vol.All(cv.time_period, cv.positive_timedelta), + }) +}, extra=vol.ALLOW_EXTRA) + +SERVICE_HORN = 'sound_horn' +ATTR_VEHICLE_INDEX = 'vehicle_index' +SERVICE_HORN_SCHEMA = vol.Schema({ + vol.Required(ATTR_VEHICLE_INDEX): cv.positive_int +}) + + +def setup(hass, config): + """Set up the Mopar component.""" + import motorparts + + cookie = hass.config.path(COOKIE_FILE) + try: + session = motorparts.get_session( + config[CONF_USERNAME], + config[CONF_PASSWORD], + config[CONF_PIN], + cookie_path=cookie + ) + except motorparts.MoparError: + _LOGGER.error("Failed to login") + return False + + data = hass.data[DOMAIN] = MoparData(hass, session) + data.update(now=None) + + track_time_interval( + hass, data.update, config[CONF_SCAN_INTERVAL] + ) + + def handle_horn(call): + """Enable the horn on the Mopar vehicle.""" + data.actuate('horn', call.data[ATTR_VEHICLE_INDEX]) + + hass.services.register( + DOMAIN, + SERVICE_HORN, + handle_horn, + schema=SERVICE_HORN_SCHEMA + ) + + for platform in SUPPORTED_PLATFORMS: + load_platform(hass, platform, DOMAIN, {}, config) + + return True + + +class MoparData: + """ + Container for Mopar vehicle data. + + Prevents session expiry re-login race condition. + """ + + def __init__(self, hass, session): + """Initialize data.""" + self._hass = hass + self._session = session + self.vehicles = [] + self.vhrs = {} + self.tow_guides = {} + + def update(self, now, **kwargs): + """Update data.""" + import motorparts + + _LOGGER.debug("Updating vehicle data") + try: + self.vehicles = motorparts.get_summary(self._session)['vehicles'] + except motorparts.MoparError: + _LOGGER.exception("Failed to get summary") + return + + for index, _ in enumerate(self.vehicles): + try: + self.vhrs[index] = motorparts.get_report(self._session, index) + self.tow_guides[index] = motorparts.get_tow_guide( + self._session, index) + except motorparts.MoparError: + _LOGGER.warning("Failed to update for vehicle index %s", index) + return + + dispatcher_send(self._hass, DATA_UPDATED) + + @property + def attribution(self): + """Get the attribution string from Mopar.""" + import motorparts + + return motorparts.ATTRIBUTION + + def get_vehicle_name(self, index): + """Get the name corresponding with this vehicle.""" + vehicle = self.vehicles[index] + if not vehicle: + return None + return '{} {} {}'.format( + vehicle['year'], + vehicle['make'], + vehicle['model'] + ) + + def actuate(self, command, index): + """Run a command on the specified Mopar vehicle.""" + import motorparts + + try: + response = getattr(motorparts, command)(self._session, index) + except motorparts.MoparError as error: + _LOGGER.error(error) + return False + + return response == SUCCESS_RESPONSE diff --git a/homeassistant/components/mopar/lock.py b/homeassistant/components/mopar/lock.py new file mode 100644 index 00000000000000..aa2e0161813188 --- /dev/null +++ b/homeassistant/components/mopar/lock.py @@ -0,0 +1,55 @@ +"""Support for the Mopar vehicle lock.""" +import logging + +from homeassistant.components.lock import LockDevice +from homeassistant.components.mopar import ( + DOMAIN as MOPAR_DOMAIN +) +from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED + +DEPENDENCIES = ['mopar'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Mopar lock platform.""" + data = hass.data[MOPAR_DOMAIN] + add_entities([MoparLock(data, index) + for index, _ in enumerate(data.vehicles)], True) + + +class MoparLock(LockDevice): + """Representation of a Mopar vehicle lock.""" + + def __init__(self, data, index): + """Initialize the Mopar lock.""" + self._index = index + self._name = '{} Lock'.format(data.get_vehicle_name(self._index)) + self._actuate = data.actuate + self._state = None + + @property + def name(self): + """Return the name of the lock.""" + return self._name + + @property + def is_locked(self): + """Return true if vehicle is locked.""" + return self._state == STATE_LOCKED + + @property + def should_poll(self): + """Return the polling requirement for this lock.""" + return False + + def lock(self, **kwargs): + """Lock the vehicle.""" + if self._actuate('lock', self._index): + self._state = STATE_LOCKED + + def unlock(self, **kwargs): + """Unlock the vehicle.""" + if self._actuate('unlock', self._index): + self._state = STATE_UNLOCKED diff --git a/homeassistant/components/mopar/sensor.py b/homeassistant/components/mopar/sensor.py index e2dda136244373..0d6e5765fda9aa 100644 --- a/homeassistant/components/mopar/sensor.py +++ b/homeassistant/components/mopar/sensor.py @@ -1,108 +1,27 @@ -""" -Sensor for Mopar vehicles. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mopar/ -""" -from datetime import timedelta -import logging - -import voluptuous as vol - -from homeassistant.components.sensor import DOMAIN, PLATFORM_SCHEMA +"""Support for the Mopar vehicle sensor platform.""" +from homeassistant.components.mopar import ( + DOMAIN as MOPAR_DOMAIN, + DATA_UPDATED, + ATTR_VEHICLE_INDEX +) from homeassistant.const import ( - ATTR_ATTRIBUTION, ATTR_COMMAND, CONF_PASSWORD, CONF_PIN, CONF_USERNAME, - LENGTH_KILOMETERS) -import homeassistant.helpers.config_validation as cv + ATTR_ATTRIBUTION, LENGTH_KILOMETERS) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from homeassistant.util import Throttle - -REQUIREMENTS = ['motorparts==1.1.0'] - -_LOGGER = logging.getLogger(__name__) - -ATTR_VEHICLE_INDEX = 'vehicle_index' -COOKIE_FILE = 'mopar_cookies.pickle' +DEPENDENCIES = ['mopar'] +ICON = 'mdi:car' -MIN_TIME_BETWEEN_UPDATES = timedelta(days=7) -SERVICE_REMOTE_COMMAND = 'mopar_remote_command' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_PIN): cv.positive_int, -}) - -REMOTE_COMMAND_SCHEMA = vol.Schema({ - vol.Required(ATTR_COMMAND): cv.string, - vol.Required(ATTR_VEHICLE_INDEX): cv.positive_int -}) - - -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform(hass, config, add_entities, + discovery_info=None): """Set up the Mopar platform.""" - import motorparts - cookie = hass.config.path(COOKIE_FILE) - try: - session = motorparts.get_session( - config.get(CONF_USERNAME), config.get(CONF_PASSWORD), - config.get(CONF_PIN), cookie_path=cookie) - except motorparts.MoparError: - _LOGGER.error("Failed to login") - return - - def _handle_service(service): - """Handle service call.""" - index = service.data.get(ATTR_VEHICLE_INDEX) - command = service.data.get(ATTR_COMMAND) - try: - motorparts.remote_command(session, command, index) - except motorparts.MoparError as error: - _LOGGER.error(str(error)) - - hass.services.register(DOMAIN, SERVICE_REMOTE_COMMAND, _handle_service, - schema=REMOTE_COMMAND_SCHEMA) - - data = MoparData(session) + data = hass.data[MOPAR_DOMAIN] add_entities([MoparSensor(data, index) for index, _ in enumerate(data.vehicles)], True) -class MoparData: - """Container for Mopar vehicle data. - - Prevents session expiry re-login race condition. - """ - - def __init__(self, session): - """Initialize data.""" - self._session = session - self.vehicles = [] - self.vhrs = {} - self.tow_guides = {} - self.update() - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self, **kwargs): - """Update data.""" - import motorparts - _LOGGER.info("Updating vehicle data") - try: - self.vehicles = motorparts.get_summary(self._session)['vehicles'] - except motorparts.MoparError: - _LOGGER.exception("Failed to get summary") - return - for index, _ in enumerate(self.vehicles): - try: - self.vhrs[index] = motorparts.get_report(self._session, index) - self.tow_guides[index] = motorparts.get_tow_guide( - self._session, index) - except motorparts.MoparError: - _LOGGER.warning("Failed to update for vehicle index %s", index) - - class MoparSensor(Entity): """Mopar vehicle sensor.""" @@ -114,24 +33,12 @@ def __init__(self, data, index): self._tow_guide = {} self._odometer = None self._data = data - - def update(self): - """Update device state.""" - self._data.update() - self._vehicle = self._data.vehicles[self._index] - self._vhr = self._data.vhrs.get(self._index, {}) - self._tow_guide = self._data.tow_guides.get(self._index, {}) - if 'odometer' in self._vhr: - odo = float(self._vhr['odometer']) - self._odometer = int(self.hass.config.units.length( - odo, LENGTH_KILOMETERS)) + self._name = self._data.get_vehicle_name(self._index) @property def name(self): """Return the name of the sensor.""" - return '{} {} {}'.format( - self._vehicle['year'], self._vehicle['make'], - self._vehicle['model']) + return self._name @property def state(self): @@ -141,10 +48,9 @@ def state(self): @property def device_state_attributes(self): """Return the state attributes.""" - import motorparts attributes = { ATTR_VEHICLE_INDEX: self._index, - ATTR_ATTRIBUTION: motorparts.ATTRIBUTION + ATTR_ATTRIBUTION: self._data.attribution } attributes.update(self._vehicle) attributes.update(self._vhr) @@ -159,4 +65,29 @@ def unit_of_measurement(self): @property def icon(self): """Return the icon.""" - return 'mdi:car' + return ICON + + @property + def should_poll(self): + """Return the polling requirement for this sensor.""" + return False + + async def async_added_to_hass(self): + """Handle entity which will be added.""" + async_dispatcher_connect( + self.hass, DATA_UPDATED, self._schedule_immediate_update + ) + + def update(self): + """Update device state.""" + self._vehicle = self._data.vehicles[self._index] + self._vhr = self._data.vhrs.get(self._index, {}) + self._tow_guide = self._data.tow_guides.get(self._index, {}) + if 'odometer' in self._vhr: + odo = float(self._vhr['odometer']) + self._odometer = int(self.hass.config.units.length( + odo, LENGTH_KILOMETERS)) + + @callback + def _schedule_immediate_update(self): + self.async_schedule_update_ha_state(True) diff --git a/homeassistant/components/mopar/services.yaml b/homeassistant/components/mopar/services.yaml new file mode 100644 index 00000000000000..7915aefcb0fb6b --- /dev/null +++ b/homeassistant/components/mopar/services.yaml @@ -0,0 +1,6 @@ +sound_horn: + description: Trigger the vehicle's horn + fields: + vehicle_index: + description: The index of the vehicle to trigger. This is exposed in the sensor's device attributes. + example: 1 \ No newline at end of file diff --git a/homeassistant/components/mopar/switch.py b/homeassistant/components/mopar/switch.py new file mode 100644 index 00000000000000..352cdafbd417ab --- /dev/null +++ b/homeassistant/components/mopar/switch.py @@ -0,0 +1,53 @@ +"""Support for the Mopar vehicle switch.""" +import logging + +from homeassistant.components.mopar import DOMAIN as MOPAR_DOMAIN +from homeassistant.components.switch import SwitchDevice +from homeassistant.const import STATE_ON, STATE_OFF + +DEPENDENCIES = ['mopar'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Mopar Switch platform.""" + data = hass.data[MOPAR_DOMAIN] + add_entities([MoparSwitch(data, index) + for index, _ in enumerate(data.vehicles)], True) + + +class MoparSwitch(SwitchDevice): + """Representation of a Mopar switch.""" + + def __init__(self, data, index): + """Initialize the Switch.""" + self._index = index + self._name = '{} Switch'.format(data.get_vehicle_name(self._index)) + self._actuate = data.actuate + self._state = None + + @property + def name(self): + """Return the name of the switch.""" + return self._name + + @property + def is_on(self): + """Return True if the entity is on.""" + return self._state == STATE_ON + + @property + def should_poll(self): + """Return the polling requirement for this switch.""" + return False + + def turn_on(self, **kwargs): + """Turn on the Mopar Vehicle.""" + if self._actuate('engine_on', self._index): + self._state = STATE_ON + + def turn_off(self, **kwargs): + """Turn off the Mopar Vehicle.""" + if self._actuate('engine_off', self._index): + self._state = STATE_OFF diff --git a/requirements_all.txt b/requirements_all.txt index 9dfac8a213014e..664e725db6133d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -709,7 +709,7 @@ millheater==0.3.4 # homeassistant.components.mitemp_bt.sensor mitemp_bt==0.0.1 -# homeassistant.components.mopar.sensor +# homeassistant.components.mopar motorparts==1.1.0 # homeassistant.components.tts From 877714605365448aed8490944c87ba073471aeed Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 27 Mar 2019 21:14:49 -0600 Subject: [PATCH 214/605] Fix too-abrubt SimpliSafe data refresh termination on error (#22466) --- homeassistant/components/simplisafe/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 73a4c61a6ab5a7..e6b9aba643da06 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -113,8 +113,9 @@ async def refresh(event_time): try: await system.update() except SimplipyError as err: - _LOGGER.error('There was an error while updating: %s', err) - return + _LOGGER.error( + 'There was error updating "%s": %s', system.address, err) + continue async_dispatcher_send(hass, TOPIC_UPDATE.format(system.system_id)) From 92457ca5ca846c8c2ca5007f9cc28736672285a7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 20:16:00 -0700 Subject: [PATCH 215/605] Fix syntax error --- homeassistant/components/homekit_controller/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index c777d2944c19cd..f9fd0409c9caaf 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -7,7 +7,7 @@ from homeassistant.helpers.entity import Entity from .config_flow import load_old_pairings -from .connection import get_accessory_information HKDevice +from .connection import get_accessory_information, HKDevice from .const import ( CONTROLLER, HOMEKIT_DIR, KNOWN_DEVICES, PAIRING_FILE ) From 7741ec4d5a54445255e5a83b07f62ebe15dfaabf Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 27 Mar 2019 20:36:13 -0700 Subject: [PATCH 216/605] Great migration notify (#22406) * Move notify platforms into components * Move notify tests * Fix notify tests * More fixes * Update requirements * Update .coveragerc * Run gen reqs --- .coveragerc | 879 +++++++++--------- homeassistant/components/apns/__init__.py | 1 + .../{notify/apns.py => apns/notify.py} | 2 +- .../components/aws_lambda/__init__.py | 1 + .../aws_lambda.py => aws_lambda/notify.py} | 3 +- homeassistant/components/aws_sns/__init__.py | 1 + .../{notify/aws_sns.py => aws_sns/notify.py} | 2 +- homeassistant/components/aws_sqs/__init__.py | 1 + .../{notify/aws_sqs.py => aws_sqs/notify.py} | 3 +- .../components/ciscospark/__init__.py | 1 + .../ciscospark.py => ciscospark/notify.py} | 3 +- .../components/clickatell/__init__.py | 1 + .../clickatell.py => clickatell/notify.py} | 3 +- .../components/clicksend/__init__.py | 1 + .../clicksend.py => clicksend/notify.py} | 3 +- .../components/clicksend_tts/__init__.py | 1 + .../notify.py} | 3 +- .../notify.py} | 3 +- .../{notify/demo.py => demo/notify.py} | 2 +- homeassistant/components/discord/__init__.py | 1 + .../{notify/discord.py => discord/notify.py} | 4 +- homeassistant/components/facebook/__init__.py | 1 + .../facebook.py => facebook/notify.py} | 4 +- .../{notify/file.py => file/notify.py} | 2 +- homeassistant/components/flock/__init__.py | 1 + .../{notify/flock.py => flock/notify.py} | 3 +- .../components/free_mobile/__init__.py | 1 + .../free_mobile.py => free_mobile/notify.py} | 3 +- homeassistant/components/gntp/__init__.py | 1 + .../{notify/gntp.py => gntp/notify.py} | 2 +- .../{notify/group.py => group/notify.py} | 2 +- homeassistant/components/hipchat/__init__.py | 1 + .../{notify/hipchat.py => hipchat/notify.py} | 4 +- homeassistant/components/html5/__init__.py | 1 + .../{notify/html5.py => html5/notify.py} | 2 +- .../{notify/kodi.py => kodi/notify.py} | 2 +- .../components/lannouncer/__init__.py | 1 + .../lannouncer.py => lannouncer/notify.py} | 3 +- .../components/llamalab_automate/__init__.py | 1 + .../notify.py} | 3 +- homeassistant/components/mastodon/__init__.py | 1 + .../mastodon.py => mastodon/notify.py} | 3 +- .../components/message_bird/__init__.py | 1 + .../notify.py} | 3 +- .../{notify/mycroft.py => mycroft/notify.py} | 2 +- .../components/nfandroidtv/__init__.py | 1 + .../nfandroidtv.py => nfandroidtv/notify.py} | 2 +- homeassistant/components/prowl/__init__.py | 1 + .../{notify/prowl.py => prowl/notify.py} | 2 +- .../pushbullet.py => pushbullet/notify.py} | 2 +- homeassistant/components/pushetta/__init__.py | 1 + .../pushetta.py => pushetta/notify.py} | 2 +- homeassistant/components/pushover/__init__.py | 1 + .../pushover.py => pushover/notify.py} | 2 +- .../components/pushsafer/__init__.py | 1 + .../pushsafer.py => pushsafer/notify.py} | 2 +- .../{notify/rest.py => rest/notify.py} | 2 +- .../components/rocketchat/__init__.py | 1 + .../rocketchat.py => rocketchat/notify.py} | 3 +- homeassistant/components/sendgrid/__init__.py | 1 + .../sendgrid.py => sendgrid/notify.py} | 2 +- .../components/simplepush/__init__.py | 1 + .../simplepush.py => simplepush/notify.py} | 2 +- homeassistant/components/slack/__init__.py | 1 + .../{notify/slack.py => slack/notify.py} | 2 +- homeassistant/components/smtp/__init__.py | 1 + .../{notify/smtp.py => smtp/notify.py} | 2 +- homeassistant/components/stride/__init__.py | 1 + .../{notify/stride.py => stride/notify.py} | 4 +- .../components/synology_chat/__init__.py | 1 + .../notify.py} | 3 +- homeassistant/components/syslog/__init__.py | 1 + .../{notify/syslog.py => syslog/notify.py} | 2 +- homeassistant/components/telegram/__init__.py | 1 + .../telegram.py => telegram/notify.py} | 2 +- .../components/twilio_call/__init__.py | 1 + .../twilio_call.py => twilio_call/notify.py} | 3 +- .../components/twilio_sms/__init__.py | 1 + .../twilio_sms.py => twilio_sms/notify.py} | 3 +- homeassistant/components/twitter/__init__.py | 1 + .../{notify/twitter.py => twitter/notify.py} | 3 +- homeassistant/components/xmpp/__init__.py | 1 + .../{notify/xmpp.py => xmpp/notify.py} | 2 +- homeassistant/components/yessssms/__init__.py | 1 + .../yessssms.py => yessssms/notify.py} | 3 +- requirements_all.txt | 50 +- requirements_test_all.txt | 8 +- tests/components/apns/__init__.py | 1 + .../test_apns.py => apns/test_notify.py} | 30 +- .../test_notify.py} | 2 +- .../test_demo.py => demo/test_notify.py} | 6 +- tests/components/facebook/__init__.py | 1 + .../test_notify.py} | 3 +- .../test_file.py => file/test_notify.py} | 4 +- .../test_group.py => group/test_notify.py} | 3 +- tests/components/homematic/__init__.py | 1 + .../test_notify.py} | 0 tests/components/html5/__init__.py | 1 + .../test_html5.py => html5/test_notify.py} | 26 +- tests/components/pushbullet/__init__.py | 1 + .../test_notify.py} | 0 tests/components/smtp/__init__.py | 1 + .../test_smtp.py => smtp/test_notify.py} | 4 +- tests/components/yessssms/__init__.py | 1 + .../test_notify.py} | 20 +- 105 files changed, 635 insertions(+), 564 deletions(-) create mode 100644 homeassistant/components/apns/__init__.py rename homeassistant/components/{notify/apns.py => apns/notify.py} (99%) create mode 100644 homeassistant/components/aws_lambda/__init__.py rename homeassistant/components/{notify/aws_lambda.py => aws_lambda/notify.py} (95%) create mode 100644 homeassistant/components/aws_sns/__init__.py rename homeassistant/components/{notify/aws_sns.py => aws_sns/notify.py} (98%) create mode 100644 homeassistant/components/aws_sqs/__init__.py rename homeassistant/components/{notify/aws_sqs.py => aws_sqs/notify.py} (94%) create mode 100644 homeassistant/components/ciscospark/__init__.py rename homeassistant/components/{notify/ciscospark.py => ciscospark/notify.py} (92%) create mode 100644 homeassistant/components/clickatell/__init__.py rename homeassistant/components/{notify/clickatell.py => clickatell/notify.py} (92%) create mode 100644 homeassistant/components/clicksend/__init__.py rename homeassistant/components/{notify/clicksend.py => clicksend/notify.py} (95%) create mode 100644 homeassistant/components/clicksend_tts/__init__.py rename homeassistant/components/{notify/clicksend_tts.py => clicksend_tts/notify.py} (96%) rename homeassistant/components/{notify/command_line.py => command_line/notify.py} (91%) rename homeassistant/components/{notify/demo.py => demo/notify.py} (92%) create mode 100644 homeassistant/components/discord/__init__.py rename homeassistant/components/{notify/discord.py => discord/notify.py} (91%) create mode 100644 homeassistant/components/facebook/__init__.py rename homeassistant/components/{notify/facebook.py => facebook/notify.py} (95%) rename homeassistant/components/{notify/file.py => file/notify.py} (97%) create mode 100644 homeassistant/components/flock/__init__.py rename homeassistant/components/{notify/flock.py => flock/notify.py} (93%) create mode 100644 homeassistant/components/free_mobile/__init__.py rename homeassistant/components/{notify/free_mobile.py => free_mobile/notify.py} (92%) create mode 100644 homeassistant/components/gntp/__init__.py rename homeassistant/components/{notify/gntp.py => gntp/notify.py} (98%) rename homeassistant/components/{notify/group.py => group/notify.py} (97%) create mode 100644 homeassistant/components/hipchat/__init__.py rename homeassistant/components/{notify/hipchat.py => hipchat/notify.py} (94%) create mode 100644 homeassistant/components/html5/__init__.py rename homeassistant/components/{notify/html5.py => html5/notify.py} (99%) rename homeassistant/components/{notify/kodi.py => kodi/notify.py} (98%) create mode 100644 homeassistant/components/lannouncer/__init__.py rename homeassistant/components/{notify/lannouncer.py => lannouncer/notify.py} (94%) create mode 100644 homeassistant/components/llamalab_automate/__init__.py rename homeassistant/components/{notify/llamalab_automate.py => llamalab_automate/notify.py} (93%) create mode 100644 homeassistant/components/mastodon/__init__.py rename homeassistant/components/{notify/mastodon.py => mastodon/notify.py} (93%) create mode 100644 homeassistant/components/message_bird/__init__.py rename homeassistant/components/{notify/message_bird.py => message_bird/notify.py} (93%) rename homeassistant/components/{notify/mycroft.py => mycroft/notify.py} (93%) create mode 100644 homeassistant/components/nfandroidtv/__init__.py rename homeassistant/components/{notify/nfandroidtv.py => nfandroidtv/notify.py} (99%) create mode 100644 homeassistant/components/prowl/__init__.py rename homeassistant/components/{notify/prowl.py => prowl/notify.py} (98%) rename homeassistant/components/{notify/pushbullet.py => pushbullet/notify.py} (99%) create mode 100644 homeassistant/components/pushetta/__init__.py rename homeassistant/components/{notify/pushetta.py => pushetta/notify.py} (98%) create mode 100644 homeassistant/components/pushover/__init__.py rename homeassistant/components/{notify/pushover.py => pushover/notify.py} (97%) create mode 100644 homeassistant/components/pushsafer/__init__.py rename homeassistant/components/{notify/pushsafer.py => pushsafer/notify.py} (99%) rename homeassistant/components/{notify/rest.py => rest/notify.py} (99%) create mode 100644 homeassistant/components/rocketchat/__init__.py rename homeassistant/components/{notify/rocketchat.py => rocketchat/notify.py} (94%) create mode 100644 homeassistant/components/sendgrid/__init__.py rename homeassistant/components/{notify/sendgrid.py => sendgrid/notify.py} (98%) create mode 100644 homeassistant/components/simplepush/__init__.py rename homeassistant/components/{notify/simplepush.py => simplepush/notify.py} (97%) create mode 100644 homeassistant/components/slack/__init__.py rename homeassistant/components/{notify/slack.py => slack/notify.py} (99%) create mode 100644 homeassistant/components/smtp/__init__.py rename homeassistant/components/{notify/smtp.py => smtp/notify.py} (99%) create mode 100644 homeassistant/components/stride/__init__.py rename homeassistant/components/{notify/stride.py => stride/notify.py} (93%) create mode 100644 homeassistant/components/synology_chat/__init__.py rename homeassistant/components/{notify/synology_chat.py => synology_chat/notify.py} (92%) create mode 100644 homeassistant/components/syslog/__init__.py rename homeassistant/components/{notify/syslog.py => syslog/notify.py} (98%) create mode 100644 homeassistant/components/telegram/__init__.py rename homeassistant/components/{notify/telegram.py => telegram/notify.py} (98%) create mode 100644 homeassistant/components/twilio_call/__init__.py rename homeassistant/components/{notify/twilio_call.py => twilio_call/notify.py} (92%) create mode 100644 homeassistant/components/twilio_sms/__init__.py rename homeassistant/components/{notify/twilio_sms.py => twilio_sms/notify.py} (91%) create mode 100644 homeassistant/components/twitter/__init__.py rename homeassistant/components/{notify/twitter.py => twitter/notify.py} (98%) create mode 100644 homeassistant/components/xmpp/__init__.py rename homeassistant/components/{notify/xmpp.py => xmpp/notify.py} (99%) create mode 100644 homeassistant/components/yessssms/__init__.py rename homeassistant/components/{notify/yessssms.py => yessssms/notify.py} (95%) create mode 100644 tests/components/apns/__init__.py rename tests/components/{notify/test_apns.py => apns/test_notify.py} (92%) rename tests/components/{notify/test_command_line.py => command_line/test_notify.py} (97%) rename tests/components/{notify/test_demo.py => demo/test_notify.py} (97%) create mode 100644 tests/components/facebook/__init__.py rename tests/components/{notify/test_facebook.py => facebook/test_notify.py} (97%) rename tests/components/{notify/test_file.py => file/test_notify.py} (96%) rename tests/components/{notify/test_group.py => group/test_notify.py} (96%) create mode 100644 tests/components/homematic/__init__.py rename tests/components/{notify/test_homematic.py => homematic/test_notify.py} (100%) create mode 100644 tests/components/html5/__init__.py rename tests/components/{notify/test_html5.py => html5/test_notify.py} (94%) create mode 100644 tests/components/pushbullet/__init__.py rename tests/components/{notify/test_pushbullet.py => pushbullet/test_notify.py} (100%) create mode 100644 tests/components/smtp/__init__.py rename tests/components/{notify/test_smtp.py => smtp/test_notify.py} (96%) create mode 100644 tests/components/yessssms/__init__.py rename tests/components/{notify/test_yessssms.py => yessssms/test_notify.py} (90%) diff --git a/.coveragerc b/.coveragerc index 2cfac103f3ca32..3cba85193140e5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,165 +3,165 @@ source = homeassistant omit = homeassistant/__main__.py + homeassistant/helpers/signal.py + homeassistant/helpers/typing.py + homeassistant/monkey_patch.py homeassistant/scripts/*.py homeassistant/util/async.py - homeassistant/monkey_patch.py - homeassistant/helpers/typing.py - homeassistant/helpers/signal.py # omit pieces of code that rely on external devices being present homeassistant/components/abode/* + homeassistant/components/acer_projector/switch.py + homeassistant/components/actiontec/device_tracker.py homeassistant/components/ads/* - homeassistant/components/nilu/air_quality.py - homeassistant/components/norway_air/air_quality.py - homeassistant/components/opensensemap/air_quality.py - homeassistant/components/alarmdotcom/alarm_control_panel.py - homeassistant/components/canary/alarm_control_panel.py - homeassistant/components/concord232/alarm_control_panel.py - homeassistant/components/ialarm/alarm_control_panel.py + homeassistant/components/aftership/sensor.py + homeassistant/components/airvisual/sensor.py + homeassistant/components/aladdin_connect/cover.py homeassistant/components/alarm_control_panel/manual_mqtt.py - homeassistant/components/nx584/alarm_control_panel.py - homeassistant/components/totalconnect/alarm_control_panel.py - homeassistant/components/yale_smart_alarm/alarm_control_panel.py homeassistant/components/alarmdecoder/* + homeassistant/components/alarmdotcom/alarm_control_panel.py + homeassistant/components/alpha_vantage/sensor.py homeassistant/components/ambient_station/* homeassistant/components/amcrest/* homeassistant/components/android_ip_webcam/* homeassistant/components/androidtv/* + homeassistant/components/anel_pwrctrl/switch.py + homeassistant/components/anthemav/media_player.py homeassistant/components/apcupsd/* homeassistant/components/apiai/* homeassistant/components/apple_tv/* homeassistant/components/aqualogic/* + homeassistant/components/aquostv/media_player.py homeassistant/components/arduino/* + homeassistant/components/arest/binary_sensor.py + homeassistant/components/arest/sensor.py + homeassistant/components/arest/switch.py homeassistant/components/arlo/* + homeassistant/components/aruba/device_tracker.py + homeassistant/components/arwn/sensor.py + homeassistant/components/asterisk_cdr/mailbox.py homeassistant/components/asterisk_mbox/* + homeassistant/components/asuswrt/device_tracker.py homeassistant/components/august/* + homeassistant/components/automatic/device_tracker.py + homeassistant/components/avion/light.py + homeassistant/components/aws_lambda/notify.py + homeassistant/components/aws_sns/notify.py + homeassistant/components/aws_sqs/notify.py homeassistant/components/bbb_gpio/* - homeassistant/components/arest/binary_sensor.py - homeassistant/components/concord232/binary_sensor.py - homeassistant/components/flic/binary_sensor.py - homeassistant/components/hikvision/binary_sensor.py - homeassistant/components/iss/binary_sensor.py - homeassistant/components/mystrom/binary_sensor.py - homeassistant/components/ping/binary_sensor.py - homeassistant/components/rest/binary_sensor.py - homeassistant/components/tapsaff/binary_sensor.py - homeassistant/components/uptimerobot/binary_sensor.py + homeassistant/components/bbox/device_tracker.py + homeassistant/components/bbox/sensor.py + homeassistant/components/bh1750/sensor.py + homeassistant/components/bitcoin/sensor.py homeassistant/components/blink/* + homeassistant/components/blinksticklight/light.py + homeassistant/components/blinkt/light.py + homeassistant/components/blockchain/sensor.py homeassistant/components/bloomsky/* + homeassistant/components/bluesound/media_player.py + homeassistant/components/bluetooth_le_tracker/device_tracker.py + homeassistant/components/bluetooth_tracker/device_tracker.py + homeassistant/components/bme280/sensor.py + homeassistant/components/bme680/sensor.py homeassistant/components/bmw_connected_drive/* + homeassistant/components/bom/sensor.py + homeassistant/components/bom/weather.py + homeassistant/components/braviatv/media_player.py + homeassistant/components/broadlink/sensor.py + homeassistant/components/broadlink/switch.py + homeassistant/components/brottsplatskartan/sensor.py homeassistant/components/browser/* + homeassistant/components/brunt/cover.py + homeassistant/components/bt_home_hub_5/device_tracker.py + homeassistant/components/bt_smarthub/device_tracker.py + homeassistant/components/buienradar/sensor.py + homeassistant/components/buienradar/weather.py homeassistant/components/caldav/calendar.py - homeassistant/components/todoist/calendar.py - homeassistant/components/bloomsky/camera.py + homeassistant/components/canary/alarm_control_panel.py homeassistant/components/canary/camera.py - homeassistant/components/familyhub/camera.py - homeassistant/components/ffmpeg/camera.py - homeassistant/components/foscam/camera.py - homeassistant/components/mjpeg/camera.py - homeassistant/components/onvif/camera.py - homeassistant/components/proxy/camera.py - homeassistant/components/ring/camera.py - homeassistant/components/rpi_camera/camera.py - homeassistant/components/synology/camera.py - homeassistant/components/xeoma/camera.py - homeassistant/components/xiaomi/camera.py - homeassistant/components/yi/camera.py homeassistant/components/cast/* + homeassistant/components/cert_expiry/sensor.py + homeassistant/components/channels/media_player.py + homeassistant/components/cisco_ios/device_tracker.py homeassistant/components/cisco_mobility_express/device_tracker.py - homeassistant/components/coolmaster/climate.py - homeassistant/components/ephember/climate.py - homeassistant/components/eq3btsmart/climate.py - homeassistant/components/flexit/climate.py - homeassistant/components/heatmiser/climate.py - homeassistant/components/homematic/climate.py - homeassistant/components/honeywell/climate.py - homeassistant/components/knx/climate.py - homeassistant/components/mill/climate.py - homeassistant/components/oem/climate.py - homeassistant/components/proliphix/climate.py - homeassistant/components/radiotherm/climate.py - homeassistant/components/sensibo/climate.py - homeassistant/components/tfiac/climate.py - homeassistant/components/touchline/climate.py - homeassistant/components/venstar/climate.py - homeassistant/components/zhong_hong/climate.py + homeassistant/components/ciscospark/notify.py + homeassistant/components/citybikes/sensor.py + homeassistant/components/clementine/media_player.py + homeassistant/components/clickatell/notify.py + homeassistant/components/clicksend/notify.py + homeassistant/components/clicksend_tts/notify.py homeassistant/components/cloudflare/* + homeassistant/components/cmus/media_player.py homeassistant/components/coinbase/* + homeassistant/components/comed_hourly_pricing/sensor.py homeassistant/components/comfoconnect/* - homeassistant/components/aladdin_connect/cover.py - homeassistant/components/brunt/cover.py - homeassistant/components/garadget/cover.py - homeassistant/components/gogogate2/cover.py - homeassistant/components/homematic/cover.py - homeassistant/components/knx/cover.py - homeassistant/components/myq/cover.py - homeassistant/components/opengarage/cover.py - homeassistant/components/rpi_gpio/cover.py - homeassistant/components/scsgate/cover.py + homeassistant/components/concord232/alarm_control_panel.py + homeassistant/components/concord232/binary_sensor.py + homeassistant/components/coolmaster/climate.py + homeassistant/components/cppm_tracker/device_tracker.py + homeassistant/components/cpuspeed/sensor.py + homeassistant/components/crimereports/sensor.py + homeassistant/components/cups/sensor.py + homeassistant/components/currencylayer/sensor.py homeassistant/components/daikin/* homeassistant/components/danfoss_air/* - homeassistant/components/actiontec/device_tracker.py - homeassistant/components/aruba/device_tracker.py - homeassistant/components/asuswrt/device_tracker.py - homeassistant/components/automatic/device_tracker.py - homeassistant/components/bbox/device_tracker.py - homeassistant/components/bluetooth_le_tracker/device_tracker.py - homeassistant/components/bluetooth_tracker/device_tracker.py - homeassistant/components/bt_home_hub_5/device_tracker.py - homeassistant/components/bt_smarthub/device_tracker.py - homeassistant/components/cisco_ios/device_tracker.py - homeassistant/components/cppm_tracker/device_tracker.py + homeassistant/components/darksky/weather.py homeassistant/components/ddwrt/device_tracker.py - homeassistant/components/fritz/device_tracker.py - homeassistant/components/google_maps/device_tracker.py - homeassistant/components/hitron_coda/device_tracker.py - homeassistant/components/huawei_router/device_tracker.py - homeassistant/components/icloud/device_tracker.py - homeassistant/components/keenetic_ndms2/device_tracker.py - homeassistant/components/linksys_ap/device_tracker.py - homeassistant/components/linksys_smart/device_tracker.py - homeassistant/components/luci/device_tracker.py - homeassistant/components/mikrotik/device_tracker.py - homeassistant/components/netgear/device_tracker.py - homeassistant/components/nmap_tracker/device_tracker.py - homeassistant/components/ping/device_tracker.py - homeassistant/components/quantum_gateway/device_tracker.py - homeassistant/components/ritassist/device_tracker.py - homeassistant/components/sky_hub/device_tracker.py - homeassistant/components/snmp/device_tracker.py - homeassistant/components/swisscom/device_tracker.py - homeassistant/components/synology_srm/device_tracker.py - homeassistant/components/tado/device_tracker.py - homeassistant/components/thomson/device_tracker.py - homeassistant/components/tile/device_tracker.py - homeassistant/components/tomato/device_tracker.py - homeassistant/components/tplink/device_tracker.py - homeassistant/components/traccar/device_tracker.py - homeassistant/components/trackr/device_tracker.py - homeassistant/components/ubee/device_tracker.py - homeassistant/components/ubus/device_tracker.py - homeassistant/components/xfinity/device_tracker.py + homeassistant/components/decora/light.py + homeassistant/components/decora_wifi/light.py + homeassistant/components/deluge/sensor.py + homeassistant/components/deluge/switch.py + homeassistant/components/denon/media_player.py + homeassistant/components/denonavr/media_player.py + homeassistant/components/deutsche_bahn/sensor.py + homeassistant/components/dht/sensor.py homeassistant/components/digital_ocean/* + homeassistant/components/digitalloggers/switch.py + homeassistant/components/directv/media_player.py + homeassistant/components/discogs/sensor.py + homeassistant/components/discord/notify.py + homeassistant/components/dlib_face_detect/image_processing.py + homeassistant/components/dlib_face_identify/image_processing.py + homeassistant/components/dlink/switch.py + homeassistant/components/dlna_dmr/media_player.py + homeassistant/components/dnsip/sensor.py + homeassistant/components/domain_expiry/sensor.py homeassistant/components/dominos/* homeassistant/components/doorbird/* homeassistant/components/dovado/* homeassistant/components/downloader/* + homeassistant/components/dte_energy_bridge/sensor.py + homeassistant/components/dublin_bus_transport/sensor.py + homeassistant/components/duke_energy/sensor.py + homeassistant/components/dunehd/media_player.py + homeassistant/components/dwd_weather_warnings/sensor.py homeassistant/components/dweet/* + homeassistant/components/ebox/sensor.py homeassistant/components/ebusd/* homeassistant/components/ecoal_boiler/* homeassistant/components/ecobee/* + homeassistant/components/econet/water_heater.py homeassistant/components/ecovacs/* + homeassistant/components/eddystone_temperature/sensor.py + homeassistant/components/edimax/switch.py homeassistant/components/edp_redy/* homeassistant/components/egardia/* homeassistant/components/eight_sleep/* + homeassistant/components/eliqonline/sensor.py homeassistant/components/elkm1/* + homeassistant/components/emby/media_player.py + homeassistant/components/emoncms/sensor.py homeassistant/components/emoncms_history/* homeassistant/components/emulated_hue/upnp.py homeassistant/components/enigma2/media_player.py homeassistant/components/enocean/* + homeassistant/components/enphase_envoy/sensor.py homeassistant/components/entur_public_transport/* + homeassistant/components/envirophat/sensor.py homeassistant/components/envisalink/* + homeassistant/components/ephember/climate.py + homeassistant/components/epson/media_player.py + homeassistant/components/eq3btsmart/climate.py homeassistant/components/esphome/__init__.py homeassistant/components/esphome/binary_sensor.py homeassistant/components/esphome/cover.py @@ -169,514 +169,509 @@ omit = homeassistant/components/esphome/light.py homeassistant/components/esphome/sensor.py homeassistant/components/esphome/switch.py + homeassistant/components/etherscan/sensor.py homeassistant/components/eufy/* + homeassistant/components/everlights/light.py homeassistant/components/evohome/* - homeassistant/components/wemo/fan.py + homeassistant/components/familyhub/camera.py homeassistant/components/fastdotcom/* + homeassistant/components/fedex/sensor.py + homeassistant/components/ffmpeg/camera.py homeassistant/components/fibaro/* + homeassistant/components/filesize/sensor.py + homeassistant/components/fints/sensor.py + homeassistant/components/fitbit/sensor.py + homeassistant/components/fixer/sensor.py + homeassistant/components/flexit/climate.py + homeassistant/components/flic/binary_sensor.py + homeassistant/components/flock/notify.py + homeassistant/components/flunearyou/sensor.py + homeassistant/components/flux_led/light.py + homeassistant/components/folder/sensor.py homeassistant/components/folder_watcher/* + homeassistant/components/foobot/sensor.py + homeassistant/components/foscam/camera.py homeassistant/components/foursquare/* + homeassistant/components/free_mobile/notify.py homeassistant/components/freebox/* + homeassistant/components/fritz/device_tracker.py homeassistant/components/fritzbox/* + homeassistant/components/fritzbox_callmonitor/sensor.py + homeassistant/components/fritzbox_netmonitor/sensor.py + homeassistant/components/fritzdect/switch.py + homeassistant/components/frontier_silicon/media_player.py + homeassistant/components/futurenow/light.py + homeassistant/components/garadget/cover.py homeassistant/components/gc100/* + homeassistant/components/gearbest/sensor.py + homeassistant/components/geizhals/sensor.py + homeassistant/components/github/sensor.py + homeassistant/components/gitlab_ci/sensor.py + homeassistant/components/gitter/sensor.py + homeassistant/components/glances/sensor.py + homeassistant/components/gntp/notify.py homeassistant/components/goalfeed/* + homeassistant/components/gogogate2/cover.py homeassistant/components/google/* + homeassistant/components/google_maps/device_tracker.py + homeassistant/components/google_travel_time/sensor.py homeassistant/components/googlehome/* + homeassistant/components/gpmdp/media_player.py + homeassistant/components/gpsd/sensor.py homeassistant/components/greeneye_monitor/* + homeassistant/components/greeneye_monitor/sensor.py + homeassistant/components/greenwave/light.py + homeassistant/components/group/notify.py + homeassistant/components/gstreamer/media_player.py + homeassistant/components/gtfs/sensor.py + homeassistant/components/gtt/sensor.py homeassistant/components/habitica/* - homeassistant/components/hangouts/__init__.py homeassistant/components/hangouts/* + homeassistant/components/hangouts/__init__.py homeassistant/components/hangouts/const.py homeassistant/components/hangouts/hangouts_bot.py homeassistant/components/hangouts/hangups_utils.py + homeassistant/components/harman_kardon_avr/media_player.py + homeassistant/components/harmony/remote.py + homeassistant/components/haveibeenpwned/sensor.py homeassistant/components/hdmi_cec/* + homeassistant/components/heatmiser/climate.py + homeassistant/components/hikvision/binary_sensor.py + homeassistant/components/hikvisioncam/switch.py + homeassistant/components/hipchat/notify.py + homeassistant/components/hitron_coda/device_tracker.py homeassistant/components/hive/* homeassistant/components/hlk_sw16/* homeassistant/components/homekit_controller/* homeassistant/components/homematic/* + homeassistant/components/homematic/climate.py + homeassistant/components/homematic/cover.py + homeassistant/components/homematic/notify.py homeassistant/components/homematicip_cloud/* homeassistant/components/homeworks/* + homeassistant/components/honeywell/climate.py + homeassistant/components/hook/switch.py + homeassistant/components/horizon/media_player.py + homeassistant/components/hp_ilo/sensor.py + homeassistant/components/htu21d/sensor.py homeassistant/components/huawei_lte/* + homeassistant/components/huawei_router/device_tracker.py + homeassistant/components/hue/light.py + homeassistant/components/hunterdouglas_powerview/scene.py homeassistant/components/hydrawise/* + homeassistant/components/hyperion/light.py + homeassistant/components/ialarm/alarm_control_panel.py + homeassistant/components/icloud/device_tracker.py homeassistant/components/idteck_prox/* homeassistant/components/ifttt/* + homeassistant/components/iglo/light.py homeassistant/components/ihc/* - homeassistant/components/dlib_face_detect/image_processing.py - homeassistant/components/dlib_face_identify/image_processing.py - homeassistant/components/qrcode/image_processing.py - homeassistant/components/seven_segments/image_processing.py - homeassistant/components/tensorflow/image_processing.py + homeassistant/components/iliad_italy/sensor.py + homeassistant/components/imap/sensor.py + homeassistant/components/imap_email_content/sensor.py + homeassistant/components/influxdb/sensor.py + homeassistant/components/insteon/* homeassistant/components/insteon_local/* homeassistant/components/insteon_plm/* - homeassistant/components/insteon/* homeassistant/components/ios/* homeassistant/components/iota/* homeassistant/components/iperf3/* + homeassistant/components/irish_rail_transport/sensor.py + homeassistant/components/iss/binary_sensor.py homeassistant/components/isy994/* + homeassistant/components/itach/remote.py + homeassistant/components/itunes/media_player.py homeassistant/components/joaoapps_join/* homeassistant/components/juicenet/* - homeassistant/components/keyboard_remote/* + homeassistant/components/kankun/switch.py + homeassistant/components/keenetic_ndms2/device_tracker.py homeassistant/components/keyboard/* + homeassistant/components/keyboard_remote/* homeassistant/components/kira/* + homeassistant/components/kiwi/lock.py homeassistant/components/knx/* + homeassistant/components/knx/climate.py + homeassistant/components/knx/cover.py + homeassistant/components/kodi/media_player.py + homeassistant/components/kodi/notify.py homeassistant/components/konnected/* + homeassistant/components/kwb/sensor.py + homeassistant/components/lacrosse/sensor.py homeassistant/components/lametric/* + homeassistant/components/lannouncer/notify.py + homeassistant/components/lastfm/sensor.py + homeassistant/components/launch_library/sensor.py homeassistant/components/lcn/* + homeassistant/components/lg_netcast/media_player.py + homeassistant/components/lg_soundbar/media_player.py homeassistant/components/lifx/* - homeassistant/components/avion/light.py - homeassistant/components/blinksticklight/light.py - homeassistant/components/blinkt/light.py - homeassistant/components/decora_wifi/light.py - homeassistant/components/decora/light.py - homeassistant/components/everlights/light.py - homeassistant/components/flux_led/light.py - homeassistant/components/futurenow/light.py - homeassistant/components/greenwave/light.py - homeassistant/components/hue/light.py - homeassistant/components/hyperion/light.py - homeassistant/components/iglo/light.py + homeassistant/components/lifx_cloud/scene.py homeassistant/components/lifx_legacy/light.py - homeassistant/components/limitlessled/light.py - homeassistant/components/lw12wifi/light.py - homeassistant/components/mystrom/light.py - homeassistant/components/nanoleaf/light.py - homeassistant/components/niko_home_control/light.py - homeassistant/components/opple/light.py - homeassistant/components/osramlightify/light.py - homeassistant/components/piglow/light.py - homeassistant/components/rpi_gpio_pwm/light.py - homeassistant/components/sensehat/light.py - homeassistant/components/tikteck/light.py - homeassistant/components/tplink/light.py - homeassistant/components/tradfri/light.py - homeassistant/components/x10/light.py - homeassistant/components/yeelight/light.py - homeassistant/components/yeelightsunflower/light.py - homeassistant/components/zengge/light.py homeassistant/components/lightwave/* + homeassistant/components/limitlessled/light.py + homeassistant/components/linksys_ap/device_tracker.py + homeassistant/components/linksys_smart/device_tracker.py + homeassistant/components/linky/sensor.py homeassistant/components/linode/* + homeassistant/components/linux_battery/sensor.py homeassistant/components/lirc/* - homeassistant/components/kiwi/lock.py + homeassistant/components/liveboxplaytv/media_player.py + homeassistant/components/llamalab_automate/notify.py homeassistant/components/lockitron/lock.py - homeassistant/components/nello/lock.py - homeassistant/components/nuki/lock.py - homeassistant/components/sesame/lock.py homeassistant/components/logi_circle/* + homeassistant/components/london_underground/sensor.py + homeassistant/components/loopenergy/sensor.py + homeassistant/components/luci/device_tracker.py homeassistant/components/luftdaten/* homeassistant/components/lupusec/* - homeassistant/components/lutron_caseta/* homeassistant/components/lutron/* - homeassistant/components/asterisk_cdr/mailbox.py + homeassistant/components/lutron_caseta/* + homeassistant/components/lw12wifi/light.py + homeassistant/components/lyft/sensor.py + homeassistant/components/magicseaweed/sensor.py homeassistant/components/mailgun/notify.py homeassistant/components/map/* + homeassistant/components/mastodon/notify.py homeassistant/components/matrix/* homeassistant/components/maxcube/* homeassistant/components/media_extractor/* - homeassistant/components/anthemav/media_player.py - homeassistant/components/aquostv/media_player.py - homeassistant/components/bluesound/media_player.py - homeassistant/components/braviatv/media_player.py - homeassistant/components/channels/media_player.py - homeassistant/components/clementine/media_player.py - homeassistant/components/cmus/media_player.py - homeassistant/components/denon/media_player.py - homeassistant/components/denonavr/media_player.py - homeassistant/components/directv/media_player.py - homeassistant/components/dlna_dmr/media_player.py - homeassistant/components/dunehd/media_player.py - homeassistant/components/emby/media_player.py - homeassistant/components/epson/media_player.py - homeassistant/components/frontier_silicon/media_player.py - homeassistant/components/gpmdp/media_player.py - homeassistant/components/gstreamer/media_player.py - homeassistant/components/harman_kardon_avr/media_player.py - homeassistant/components/horizon/media_player.py - homeassistant/components/itunes/media_player.py - homeassistant/components/kodi/media_player.py - homeassistant/components/lg_netcast/media_player.py - homeassistant/components/lg_soundbar/media_player.py - homeassistant/components/liveboxplaytv/media_player.py homeassistant/components/mediaroom/media_player.py - homeassistant/components/mpchc/media_player.py - homeassistant/components/mpd/media_player.py - homeassistant/components/nad/media_player.py - homeassistant/components/nadtcp/media_player.py - homeassistant/components/onkyo/media_player.py - homeassistant/components/openhome/media_player.py - homeassistant/components/panasonic_bluray/media_player.py - homeassistant/components/panasonic_viera/media_player.py - homeassistant/components/pandora/media_player.py - homeassistant/components/philips_js/media_player.py - homeassistant/components/pioneer/media_player.py - homeassistant/components/pjlink/media_player.py - homeassistant/components/plex/media_player.py - homeassistant/components/russound_rio/media_player.py - homeassistant/components/russound_rnet/media_player.py - homeassistant/components/snapcast/media_player.py - homeassistant/components/songpal/media_player.py - homeassistant/components/spotify/media_player.py - homeassistant/components/squeezebox/media_player.py - homeassistant/components/ue_smart_radio/media_player.py - homeassistant/components/vizio/media_player.py - homeassistant/components/vlc/media_player.py - homeassistant/components/volumio/media_player.py - homeassistant/components/xiaomi_tv/media_player.py - homeassistant/components/yamaha_musiccast/media_player.py - homeassistant/components/yamaha/media_player.py - homeassistant/components/ziggo_mediabox_xl/media_player.py + homeassistant/components/message_bird/notify.py + homeassistant/components/met/weather.py homeassistant/components/meteo_france/* + homeassistant/components/metoffice/sensor.py + homeassistant/components/metoffice/weather.py + homeassistant/components/miflora/sensor.py + homeassistant/components/mikrotik/device_tracker.py + homeassistant/components/mill/climate.py + homeassistant/components/mitemp_bt/sensor.py + homeassistant/components/mjpeg/camera.py homeassistant/components/mobile_app/* homeassistant/components/mochad/* homeassistant/components/modbus/* + homeassistant/components/modem_callerid/sensor.py homeassistant/components/mopar/* + homeassistant/components/mpchc/media_player.py + homeassistant/components/mpd/media_player.py + homeassistant/components/mqtt_room/sensor.py + homeassistant/components/mvglive/sensor.py homeassistant/components/mychevy/* homeassistant/components/mycroft/* + homeassistant/components/mycroft/notify.py + homeassistant/components/myq/cover.py homeassistant/components/mysensors/* + homeassistant/components/mystrom/binary_sensor.py + homeassistant/components/mystrom/light.py + homeassistant/components/mystrom/switch.py + homeassistant/components/nad/media_player.py + homeassistant/components/nadtcp/media_player.py + homeassistant/components/nanoleaf/light.py homeassistant/components/neato/* - homeassistant/components/nest/* - homeassistant/components/netatmo/* - homeassistant/components/netgear_lte/* - homeassistant/components/nissan_leaf/* - homeassistant/components/notify/aws_lambda.py - homeassistant/components/notify/aws_sns.py - homeassistant/components/notify/aws_sqs.py - homeassistant/components/notify/ciscospark.py - homeassistant/components/notify/clickatell.py - homeassistant/components/notify/clicksend_tts.py - homeassistant/components/notify/clicksend.py - homeassistant/components/notify/discord.py - homeassistant/components/notify/flock.py - homeassistant/components/notify/free_mobile.py - homeassistant/components/notify/gntp.py - homeassistant/components/notify/group.py - homeassistant/components/notify/hipchat.py - homeassistant/components/homematic/notify.py - homeassistant/components/notify/kodi.py - homeassistant/components/notify/lannouncer.py - homeassistant/components/notify/llamalab_automate.py - homeassistant/components/notify/mastodon.py - homeassistant/components/notify/message_bird.py - homeassistant/components/notify/mycroft.py - homeassistant/components/notify/nfandroidtv.py - homeassistant/components/notify/prowl.py - homeassistant/components/notify/pushbullet.py - homeassistant/components/notify/pushetta.py - homeassistant/components/notify/pushover.py - homeassistant/components/notify/pushsafer.py - homeassistant/components/notify/rest.py - homeassistant/components/notify/rocketchat.py - homeassistant/components/notify/sendgrid.py - homeassistant/components/notify/simplepush.py - homeassistant/components/notify/slack.py - homeassistant/components/notify/smtp.py - homeassistant/components/notify/stride.py - homeassistant/components/notify/synology_chat.py - homeassistant/components/notify/syslog.py - homeassistant/components/notify/telegram.py - homeassistant/components/notify/telstra.py - homeassistant/components/notify/twilio_call.py - homeassistant/components/notify/twilio_sms.py - homeassistant/components/notify/twitter.py - homeassistant/components/notify/xmpp.py - homeassistant/components/nuimo_controller/* - homeassistant/components/octoprint/* - homeassistant/components/opencv/* - homeassistant/components/opentherm_gw/* - homeassistant/components/openuv/__init__.py - homeassistant/components/openuv/binary_sensor.py - homeassistant/components/openuv/sensor.py - homeassistant/components/owlet/* - homeassistant/components/pilight/* - homeassistant/components/plum_lightpad/* - homeassistant/components/point/* - homeassistant/components/prometheus/* - homeassistant/components/ps4/__init__.py - homeassistant/components/ps4/media_player.py - homeassistant/components/qwikswitch/* - homeassistant/components/rachio/* - homeassistant/components/rainbird/* - homeassistant/components/raincloud/* - homeassistant/components/rainmachine/__init__.py - homeassistant/components/rainmachine/binary_sensor.py - homeassistant/components/rainmachine/sensor.py - homeassistant/components/rainmachine/switch.py - homeassistant/components/raspihats/* - homeassistant/components/raspyrfm/* - homeassistant/components/reddit/* - homeassistant/components/remember_the_milk/__init__.py - homeassistant/components/harmony/remote.py - homeassistant/components/itach/remote.py - homeassistant/components/rfxtrx/* - homeassistant/components/roku/* - homeassistant/components/route53/* - homeassistant/components/rpi_gpio/* - homeassistant/components/rpi_pfio/* - homeassistant/components/sabnzbd/* - homeassistant/components/satel_integra/* - homeassistant/components/hunterdouglas_powerview/scene.py - homeassistant/components/lifx_cloud/scene.py - homeassistant/components/scsgate/* - homeassistant/components/sense/* - homeassistant/components/aftership/sensor.py - homeassistant/components/airvisual/sensor.py - homeassistant/components/alpha_vantage/sensor.py - homeassistant/components/arest/sensor.py - homeassistant/components/arwn/sensor.py - homeassistant/components/bbox/sensor.py - homeassistant/components/bh1750/sensor.py - homeassistant/components/bitcoin/sensor.py - homeassistant/components/blockchain/sensor.py - homeassistant/components/bme280/sensor.py - homeassistant/components/bme680/sensor.py - homeassistant/components/bom/sensor.py - homeassistant/components/broadlink/sensor.py - homeassistant/components/brottsplatskartan/sensor.py - homeassistant/components/buienradar/sensor.py - homeassistant/components/cert_expiry/sensor.py - homeassistant/components/citybikes/sensor.py - homeassistant/components/coinbase/sensor.py - homeassistant/components/comed_hourly_pricing/sensor.py - homeassistant/components/cpuspeed/sensor.py - homeassistant/components/crimereports/sensor.py - homeassistant/components/cups/sensor.py - homeassistant/components/currencylayer/sensor.py - homeassistant/components/deluge/sensor.py - homeassistant/components/deutsche_bahn/sensor.py - homeassistant/components/dht/sensor.py - homeassistant/components/discogs/sensor.py - homeassistant/components/dnsip/sensor.py - homeassistant/components/domain_expiry/sensor.py - homeassistant/components/dte_energy_bridge/sensor.py - homeassistant/components/dublin_bus_transport/sensor.py - homeassistant/components/duke_energy/sensor.py - homeassistant/components/dwd_weather_warnings/sensor.py - homeassistant/components/ebox/sensor.py - homeassistant/components/eddystone_temperature/sensor.py - homeassistant/components/eliqonline/sensor.py - homeassistant/components/emoncms/sensor.py - homeassistant/components/enphase_envoy/sensor.py - homeassistant/components/envirophat/sensor.py - homeassistant/components/etherscan/sensor.py - homeassistant/components/fedex/sensor.py - homeassistant/components/filesize/sensor.py - homeassistant/components/fints/sensor.py - homeassistant/components/fitbit/sensor.py - homeassistant/components/fixer/sensor.py - homeassistant/components/flunearyou/sensor.py - homeassistant/components/folder/sensor.py - homeassistant/components/foobot/sensor.py - homeassistant/components/fritzbox_callmonitor/sensor.py - homeassistant/components/fritzbox_netmonitor/sensor.py - homeassistant/components/gearbest/sensor.py - homeassistant/components/geizhals/sensor.py - homeassistant/components/github/sensor.py - homeassistant/components/gitlab_ci/sensor.py - homeassistant/components/gitter/sensor.py - homeassistant/components/glances/sensor.py - homeassistant/components/google_travel_time/sensor.py - homeassistant/components/gpsd/sensor.py - homeassistant/components/greeneye_monitor/sensor.py - homeassistant/components/gtfs/sensor.py - homeassistant/components/gtt/sensor.py - homeassistant/components/haveibeenpwned/sensor.py - homeassistant/components/hp_ilo/sensor.py - homeassistant/components/htu21d/sensor.py - homeassistant/components/iliad_italy/sensor.py - homeassistant/components/imap_email_content/sensor.py - homeassistant/components/imap/sensor.py - homeassistant/components/influxdb/sensor.py - homeassistant/components/irish_rail_transport/sensor.py - homeassistant/components/kwb/sensor.py - homeassistant/components/lacrosse/sensor.py - homeassistant/components/lastfm/sensor.py - homeassistant/components/launch_library/sensor.py - homeassistant/components/linky/sensor.py - homeassistant/components/linux_battery/sensor.py - homeassistant/components/london_underground/sensor.py - homeassistant/components/loopenergy/sensor.py - homeassistant/components/lyft/sensor.py - homeassistant/components/magicseaweed/sensor.py - homeassistant/components/metoffice/sensor.py - homeassistant/components/miflora/sensor.py - homeassistant/components/mitemp_bt/sensor.py - homeassistant/components/modem_callerid/sensor.py - homeassistant/components/mopar/* - homeassistant/components/mqtt_room/sensor.py - homeassistant/components/mvglive/sensor.py homeassistant/components/nederlandse_spoorwegen/sensor.py + homeassistant/components/nello/lock.py + homeassistant/components/nest/* + homeassistant/components/netatmo/* homeassistant/components/netatmo_public/sensor.py - homeassistant/components/netdata_public/sensor.py homeassistant/components/netdata/sensor.py + homeassistant/components/netdata_public/sensor.py + homeassistant/components/netgear/device_tracker.py + homeassistant/components/netgear_lte/* + homeassistant/components/netio/switch.py homeassistant/components/neurio_energy/sensor.py + homeassistant/components/nfandroidtv/notify.py + homeassistant/components/niko_home_control/light.py + homeassistant/components/nilu/air_quality.py + homeassistant/components/nissan_leaf/* + homeassistant/components/nmap_tracker/device_tracker.py homeassistant/components/nmbs/sensor.py homeassistant/components/noaa_tides/sensor.py + homeassistant/components/norway_air/air_quality.py homeassistant/components/nsw_fuel_station/sensor.py + homeassistant/components/nuimo_controller/* + homeassistant/components/nuki/lock.py homeassistant/components/nut/sensor.py + homeassistant/components/nx584/alarm_control_panel.py homeassistant/components/nzbget/sensor.py + homeassistant/components/octoprint/* + homeassistant/components/oem/climate.py homeassistant/components/ohmconnect/sensor.py homeassistant/components/onewire/sensor.py + homeassistant/components/onkyo/media_player.py + homeassistant/components/onvif/camera.py + homeassistant/components/opencv/* homeassistant/components/openevse/sensor.py homeassistant/components/openexchangerates/sensor.py + homeassistant/components/opengarage/cover.py + homeassistant/components/openhome/media_player.py + homeassistant/components/opensensemap/air_quality.py homeassistant/components/opensky/sensor.py + homeassistant/components/opentherm_gw/* + homeassistant/components/openuv/__init__.py + homeassistant/components/openuv/binary_sensor.py + homeassistant/components/openuv/sensor.py homeassistant/components/openweathermap/sensor.py + homeassistant/components/openweathermap/weather.py + homeassistant/components/opple/light.py + homeassistant/components/orvibo/switch.py + homeassistant/components/osramlightify/light.py homeassistant/components/otp/sensor.py + homeassistant/components/owlet/* + homeassistant/components/panasonic_bluray/media_player.py + homeassistant/components/panasonic_viera/media_player.py + homeassistant/components/pandora/media_player.py + homeassistant/components/pencom/switch.py + homeassistant/components/philips_js/media_player.py homeassistant/components/pi_hole/sensor.py + homeassistant/components/piglow/light.py + homeassistant/components/pilight/* + homeassistant/components/ping/binary_sensor.py + homeassistant/components/ping/device_tracker.py + homeassistant/components/pioneer/media_player.py + homeassistant/components/pjlink/media_player.py + homeassistant/components/plex/media_player.py homeassistant/components/plex/sensor.py + homeassistant/components/plum_lightpad/* homeassistant/components/pocketcasts/sensor.py + homeassistant/components/point/* homeassistant/components/pollen/sensor.py homeassistant/components/postnl/sensor.py homeassistant/components/prezzibenzina/sensor.py + homeassistant/components/proliphix/climate.py + homeassistant/components/prometheus/* + homeassistant/components/prowl/notify.py + homeassistant/components/proxy/camera.py + homeassistant/components/ps4/__init__.py + homeassistant/components/ps4/media_player.py + homeassistant/components/pulseaudio_loopback/switch.py + homeassistant/components/pushbullet/notify.py homeassistant/components/pushbullet/sensor.py + homeassistant/components/pushetta/notify.py + homeassistant/components/pushover/notify.py + homeassistant/components/pushsafer/notify.py homeassistant/components/pvoutput/sensor.py homeassistant/components/pyload/sensor.py homeassistant/components/qbittorrent/sensor.py homeassistant/components/qnap/sensor.py + homeassistant/components/qrcode/image_processing.py + homeassistant/components/quantum_gateway/device_tracker.py + homeassistant/components/qwikswitch/* + homeassistant/components/rachio/* homeassistant/components/radarr/sensor.py + homeassistant/components/radiotherm/climate.py + homeassistant/components/rainbird/* homeassistant/components/rainbird/sensor.py + homeassistant/components/rainbird/switch.py + homeassistant/components/raincloud/* + homeassistant/components/rainmachine/__init__.py + homeassistant/components/rainmachine/binary_sensor.py + homeassistant/components/rainmachine/sensor.py + homeassistant/components/rainmachine/switch.py + homeassistant/components/raspihats/* + homeassistant/components/raspyrfm/* homeassistant/components/recollect_waste/sensor.py + homeassistant/components/recswitch/switch.py + homeassistant/components/reddit/* homeassistant/components/rejseplanen/sensor.py + homeassistant/components/remember_the_milk/__init__.py + homeassistant/components/rest/binary_sensor.py + homeassistant/components/rest/notify.py + homeassistant/components/rest/switch.py + homeassistant/components/rfxtrx/* + homeassistant/components/ring/camera.py homeassistant/components/ripple/sensor.py + homeassistant/components/ritassist/device_tracker.py + homeassistant/components/rocketchat/notify.py + homeassistant/components/roku/* + homeassistant/components/roomba/vacuum.py + homeassistant/components/route53/* homeassistant/components/rova/sensor.py + homeassistant/components/rpi_camera/camera.py + homeassistant/components/rpi_gpio/* + homeassistant/components/rpi_gpio/cover.py + homeassistant/components/rpi_gpio_pwm/light.py + homeassistant/components/rpi_pfio/* + homeassistant/components/rpi_rf/switch.py homeassistant/components/rtorrent/sensor.py + homeassistant/components/russound_rio/media_player.py + homeassistant/components/russound_rnet/media_player.py homeassistant/components/ruter/sensor.py + homeassistant/components/sabnzbd/* + homeassistant/components/satel_integra/* homeassistant/components/scrape/sensor.py + homeassistant/components/scsgate/* + homeassistant/components/scsgate/cover.py + homeassistant/components/sendgrid/notify.py + homeassistant/components/sense/* + homeassistant/components/sensehat/light.py homeassistant/components/sensehat/sensor.py - homeassistant/components/serial_pm/sensor.py + homeassistant/components/sensibo/climate.py homeassistant/components/serial/sensor.py + homeassistant/components/serial_pm/sensor.py + homeassistant/components/sesame/lock.py + homeassistant/components/seven_segments/image_processing.py homeassistant/components/seventeentrack/sensor.py + homeassistant/components/shiftr/* homeassistant/components/shodan/sensor.py homeassistant/components/sht31/sensor.py homeassistant/components/sigfox/sensor.py + homeassistant/components/simplepush/notify.py + homeassistant/components/simplisafe/__init__.py + homeassistant/components/simplisafe/alarm_control_panel.py homeassistant/components/simulated/sensor.py + homeassistant/components/sisyphus/* + homeassistant/components/sky_hub/device_tracker.py homeassistant/components/skybeacon/sensor.py + homeassistant/components/skybell/* + homeassistant/components/slack/notify.py homeassistant/components/sma/sensor.py + homeassistant/components/smappee/* + homeassistant/components/smtp/notify.py + homeassistant/components/snapcast/media_player.py + homeassistant/components/snmp/device_tracker.py homeassistant/components/snmp/sensor.py + homeassistant/components/snmp/switch.py homeassistant/components/sochain/sensor.py homeassistant/components/socialblade/sensor.py homeassistant/components/solaredge/sensor.py homeassistant/components/sonarr/sensor.py + homeassistant/components/songpal/media_player.py + homeassistant/components/sonos/* + homeassistant/components/sony_projector/switch.py + homeassistant/components/spc/* + homeassistant/components/speedtestdotnet/* + homeassistant/components/spider/* homeassistant/components/spotcrime/sensor.py + homeassistant/components/spotify/media_player.py + homeassistant/components/squeezebox/media_player.py homeassistant/components/srp_energy/sensor.py homeassistant/components/starlingbank/sensor.py homeassistant/components/steam_online/sensor.py + homeassistant/components/stride/notify.py homeassistant/components/supervisord/sensor.py homeassistant/components/swiss_hydrological_data/sensor.py homeassistant/components/swiss_public_transport/sensor.py + homeassistant/components/swisscom/device_tracker.py + homeassistant/components/switchbot/switch.py + homeassistant/components/switchmate/switch.py homeassistant/components/syncthru/sensor.py + homeassistant/components/synology/camera.py + homeassistant/components/synology_chat/notify.py + homeassistant/components/synology_srm/device_tracker.py homeassistant/components/synologydsm/sensor.py + homeassistant/components/syslog/notify.py homeassistant/components/systemmonitor/sensor.py homeassistant/components/sytadin/sensor.py + homeassistant/components/tado/* + homeassistant/components/tado/device_tracker.py + homeassistant/components/tahoma/* homeassistant/components/tank_utility/sensor.py + homeassistant/components/tapsaff/binary_sensor.py homeassistant/components/tautulli/sensor.py homeassistant/components/ted5000/sensor.py - homeassistant/components/temper/sensor.py - homeassistant/components/thermoworks_smoke/sensor.py - homeassistant/components/time_date/sensor.py - homeassistant/components/torque/sensor.py - homeassistant/components/trafikverket_weatherstation/sensor.py - homeassistant/components/travisci/sensor.py - homeassistant/components/twitch/sensor.py - homeassistant/components/uber/sensor.py - homeassistant/components/ups/sensor.py - homeassistant/components/uscis/sensor.py - homeassistant/components/vasttrafik/sensor.py - homeassistant/components/viaggiatreno/sensor.py - homeassistant/components/volkszaehler/sensor.py - homeassistant/components/waqi/sensor.py - homeassistant/components/waze_travel_time/sensor.py - homeassistant/components/whois/sensor.py - homeassistant/components/worldtidesinfo/sensor.py - homeassistant/components/worxlandroid/sensor.py - homeassistant/components/xbox_live/sensor.py - homeassistant/components/zamg/sensor.py - homeassistant/components/zestimate/sensor.py - homeassistant/components/shiftr/* - homeassistant/components/simplisafe/__init__.py - homeassistant/components/simplisafe/alarm_control_panel.py - homeassistant/components/sisyphus/* - homeassistant/components/skybell/* - homeassistant/components/smappee/* - homeassistant/components/sonos/* - homeassistant/components/spc/* - homeassistant/components/speedtestdotnet/* - homeassistant/components/spider/* - homeassistant/components/acer_projector/switch.py - homeassistant/components/anel_pwrctrl/switch.py - homeassistant/components/arest/switch.py - homeassistant/components/broadlink/switch.py - homeassistant/components/deluge/switch.py - homeassistant/components/digitalloggers/switch.py - homeassistant/components/dlink/switch.py - homeassistant/components/edimax/switch.py - homeassistant/components/fritzdect/switch.py - homeassistant/components/hikvisioncam/switch.py - homeassistant/components/hook/switch.py - homeassistant/components/kankun/switch.py - homeassistant/components/mystrom/switch.py - homeassistant/components/netio/switch.py - homeassistant/components/orvibo/switch.py - homeassistant/components/pencom/switch.py - homeassistant/components/pulseaudio_loopback/switch.py - homeassistant/components/rainbird/switch.py - homeassistant/components/recswitch/switch.py - homeassistant/components/rest/switch.py - homeassistant/components/rpi_rf/switch.py - homeassistant/components/snmp/switch.py - homeassistant/components/sony_projector/switch.py - homeassistant/components/switchbot/switch.py - homeassistant/components/switchmate/switch.py - homeassistant/components/telnet/switch.py - homeassistant/components/tplink/switch.py - homeassistant/components/vesync/switch.py - homeassistant/components/tado/* - homeassistant/components/tahoma/* + homeassistant/components/telegram/notify.py homeassistant/components/telegram_bot/* homeassistant/components/tellduslive/* homeassistant/components/tellstick/* + homeassistant/components/telnet/switch.py + homeassistant/components/telstra/notify.py + homeassistant/components/temper/sensor.py + homeassistant/components/tensorflow/image_processing.py homeassistant/components/tesla/* + homeassistant/components/tfiac/climate.py + homeassistant/components/thermoworks_smoke/sensor.py homeassistant/components/thethingsnetwork/* homeassistant/components/thingspeak/* homeassistant/components/thinkingcleaner/* + homeassistant/components/thomson/device_tracker.py homeassistant/components/tibber/* + homeassistant/components/tikteck/light.py + homeassistant/components/tile/device_tracker.py + homeassistant/components/time_date/sensor.py + homeassistant/components/todoist/calendar.py homeassistant/components/tof/sensor.py + homeassistant/components/tomato/device_tracker.py homeassistant/components/toon/* + homeassistant/components/torque/sensor.py + homeassistant/components/totalconnect/alarm_control_panel.py + homeassistant/components/touchline/climate.py + homeassistant/components/tplink/device_tracker.py + homeassistant/components/tplink/light.py + homeassistant/components/tplink/switch.py homeassistant/components/tplink_lte/* + homeassistant/components/traccar/device_tracker.py + homeassistant/components/trackr/device_tracker.py homeassistant/components/tradfri/* + homeassistant/components/tradfri/light.py + homeassistant/components/trafikverket_weatherstation/sensor.py homeassistant/components/transmission/* + homeassistant/components/travisci/sensor.py homeassistant/components/tts/amazon_polly.py homeassistant/components/tts/baidu.py homeassistant/components/tts/microsoft.py homeassistant/components/tts/picotts.py homeassistant/components/tuya/* + homeassistant/components/twilio_call/notify.py + homeassistant/components/twilio_sms/notify.py + homeassistant/components/twitch/sensor.py + homeassistant/components/twitter/notify.py + homeassistant/components/ubee/device_tracker.py + homeassistant/components/uber/sensor.py + homeassistant/components/ubus/device_tracker.py + homeassistant/components/ue_smart_radio/media_player.py homeassistant/components/upcloud/* homeassistant/components/upnp/* + homeassistant/components/ups/sensor.py + homeassistant/components/uptimerobot/binary_sensor.py + homeassistant/components/uscis/sensor.py homeassistant/components/usps/* - homeassistant/components/roomba/vacuum.py + homeassistant/components/vasttrafik/sensor.py homeassistant/components/velbus/* homeassistant/components/velux/* + homeassistant/components/venstar/climate.py homeassistant/components/vera/* homeassistant/components/verisure/* + homeassistant/components/vesync/switch.py + homeassistant/components/viaggiatreno/sensor.py + homeassistant/components/vizio/media_player.py + homeassistant/components/vlc/media_player.py + homeassistant/components/volkszaehler/sensor.py + homeassistant/components/volumio/media_player.py homeassistant/components/volvooncall/* homeassistant/components/w800rf32/* - homeassistant/components/econet/water_heater.py + homeassistant/components/waqi/sensor.py homeassistant/components/waterfurnace/* homeassistant/components/watson_iot/* - homeassistant/components/bom/weather.py - homeassistant/components/buienradar/weather.py - homeassistant/components/darksky/weather.py - homeassistant/components/met/weather.py - homeassistant/components/metoffice/weather.py - homeassistant/components/openweathermap/weather.py - homeassistant/components/zamg/weather.py + homeassistant/components/waze_travel_time/sensor.py homeassistant/components/webostv/* homeassistant/components/wemo/* + homeassistant/components/wemo/fan.py + homeassistant/components/whois/sensor.py homeassistant/components/wink/* homeassistant/components/wirelesstag/* + homeassistant/components/worldtidesinfo/sensor.py + homeassistant/components/worxlandroid/sensor.py + homeassistant/components/x10/light.py + homeassistant/components/xbox_live/sensor.py + homeassistant/components/xeoma/camera.py + homeassistant/components/xfinity/device_tracker.py + homeassistant/components/xiaomi/camera.py homeassistant/components/xiaomi_aqara/* homeassistant/components/xiaomi_miio/* + homeassistant/components/xiaomi_tv/media_player.py + homeassistant/components/xmpp/notify.py homeassistant/components/xs1/* + homeassistant/components/yale_smart_alarm/alarm_control_panel.py + homeassistant/components/yamaha/media_player.py + homeassistant/components/yamaha_musiccast/media_player.py + homeassistant/components/yeelight/light.py + homeassistant/components/yeelightsunflower/light.py + homeassistant/components/yi/camera.py homeassistant/components/zabbix/* + homeassistant/components/zamg/sensor.py + homeassistant/components/zamg/weather.py + homeassistant/components/zengge/light.py homeassistant/components/zeroconf/* + homeassistant/components/zestimate/sensor.py homeassistant/components/zha/__init__.py homeassistant/components/zha/api.py homeassistant/components/zha/const.py @@ -689,7 +684,9 @@ omit = homeassistant/components/zha/entity.py homeassistant/components/zha/light.py homeassistant/components/zha/sensor.py + homeassistant/components/zhong_hong/climate.py homeassistant/components/zigbee/* + homeassistant/components/ziggo_mediabox_xl/media_player.py homeassistant/components/zoneminder/* homeassistant/components/zwave/util.py diff --git a/homeassistant/components/apns/__init__.py b/homeassistant/components/apns/__init__.py new file mode 100644 index 00000000000000..9332b0d1ede59b --- /dev/null +++ b/homeassistant/components/apns/__init__.py @@ -0,0 +1 @@ +"""The apns component.""" diff --git a/homeassistant/components/notify/apns.py b/homeassistant/components/apns/notify.py similarity index 99% rename from homeassistant/components/notify/apns.py rename to homeassistant/components/apns/notify.py index 3f5403f0c136fa..b2c6b63864f273 100644 --- a/homeassistant/components/notify/apns.py +++ b/homeassistant/components/apns/notify.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_state_change -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['apns2==0.3.0'] diff --git a/homeassistant/components/aws_lambda/__init__.py b/homeassistant/components/aws_lambda/__init__.py new file mode 100644 index 00000000000000..f6d86d02e93f31 --- /dev/null +++ b/homeassistant/components/aws_lambda/__init__.py @@ -0,0 +1 @@ +"""The aws_lambda component.""" diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/aws_lambda/notify.py similarity index 95% rename from homeassistant/components/notify/aws_lambda.py rename to homeassistant/components/aws_lambda/notify.py index 8f639a653c3bf8..d7ebb40d19aa62 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/aws_lambda/notify.py @@ -14,7 +14,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.json import JSONEncoder -from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['boto3==1.9.16'] diff --git a/homeassistant/components/aws_sns/__init__.py b/homeassistant/components/aws_sns/__init__.py new file mode 100644 index 00000000000000..b51698ce0dcaab --- /dev/null +++ b/homeassistant/components/aws_sns/__init__.py @@ -0,0 +1 @@ +"""The aws_sns component.""" diff --git a/homeassistant/components/notify/aws_sns.py b/homeassistant/components/aws_sns/notify.py similarity index 98% rename from homeassistant/components/notify/aws_sns.py rename to homeassistant/components/aws_sns/notify.py index 7fa0e25b32a21a..09018562cb8dcf 100644 --- a/homeassistant/components/notify/aws_sns.py +++ b/homeassistant/components/aws_sns/notify.py @@ -12,7 +12,7 @@ from homeassistant.const import CONF_NAME, CONF_PLATFORM import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/aws_sqs/__init__.py b/homeassistant/components/aws_sqs/__init__.py new file mode 100644 index 00000000000000..79b29a76009a7d --- /dev/null +++ b/homeassistant/components/aws_sqs/__init__.py @@ -0,0 +1 @@ +"""The aws_sqs component.""" diff --git a/homeassistant/components/notify/aws_sqs.py b/homeassistant/components/aws_sqs/notify.py similarity index 94% rename from homeassistant/components/notify/aws_sqs.py rename to homeassistant/components/aws_sqs/notify.py index 927824299398b3..eff9018bae9a4f 100644 --- a/homeassistant/components/notify/aws_sqs.py +++ b/homeassistant/components/aws_sqs/notify.py @@ -12,7 +12,8 @@ from homeassistant.const import CONF_NAME, CONF_PLATFORM import homeassistant.helpers.config_validation as cv -from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ["boto3==1.9.16"] diff --git a/homeassistant/components/ciscospark/__init__.py b/homeassistant/components/ciscospark/__init__.py new file mode 100644 index 00000000000000..f872a0257f7dd4 --- /dev/null +++ b/homeassistant/components/ciscospark/__init__.py @@ -0,0 +1 @@ +"""The ciscospark component.""" diff --git a/homeassistant/components/notify/ciscospark.py b/homeassistant/components/ciscospark/notify.py similarity index 92% rename from homeassistant/components/notify/ciscospark.py rename to homeassistant/components/ciscospark/notify.py index a33e9432e927e8..1eeb9b51f28486 100644 --- a/homeassistant/components/notify/ciscospark.py +++ b/homeassistant/components/ciscospark/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_TOKEN import homeassistant.helpers.config_validation as cv -from . import ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TITLE, PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['ciscosparkapi==0.4.2'] diff --git a/homeassistant/components/clickatell/__init__.py b/homeassistant/components/clickatell/__init__.py new file mode 100644 index 00000000000000..6c39bc749ada3b --- /dev/null +++ b/homeassistant/components/clickatell/__init__.py @@ -0,0 +1 @@ +"""The clickatell component.""" diff --git a/homeassistant/components/notify/clickatell.py b/homeassistant/components/clickatell/notify.py similarity index 92% rename from homeassistant/components/notify/clickatell.py rename to homeassistant/components/clickatell/notify.py index 559232e25558fe..e473e54a3b7876 100644 --- a/homeassistant/components/notify/clickatell.py +++ b/homeassistant/components/clickatell/notify.py @@ -12,7 +12,8 @@ from homeassistant.const import CONF_API_KEY, CONF_RECIPIENT import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/clicksend/__init__.py b/homeassistant/components/clicksend/__init__.py new file mode 100644 index 00000000000000..3037224b9a117c --- /dev/null +++ b/homeassistant/components/clicksend/__init__.py @@ -0,0 +1 @@ +"""The clicksend component.""" diff --git a/homeassistant/components/notify/clicksend.py b/homeassistant/components/clicksend/notify.py similarity index 95% rename from homeassistant/components/notify/clicksend.py rename to homeassistant/components/clicksend/notify.py index 6f10ac707349c3..3b2cdb7496db36 100644 --- a/homeassistant/components/notify/clicksend.py +++ b/homeassistant/components/clicksend/notify.py @@ -16,7 +16,8 @@ CONTENT_TYPE_JSON) import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/clicksend_tts/__init__.py b/homeassistant/components/clicksend_tts/__init__.py new file mode 100644 index 00000000000000..53b59309701b2e --- /dev/null +++ b/homeassistant/components/clicksend_tts/__init__.py @@ -0,0 +1 @@ +"""The clicksend_tts component.""" diff --git a/homeassistant/components/notify/clicksend_tts.py b/homeassistant/components/clicksend_tts/notify.py similarity index 96% rename from homeassistant/components/notify/clicksend_tts.py rename to homeassistant/components/clicksend_tts/notify.py index 2a7730c4a27c7c..93e5126bbab4cd 100644 --- a/homeassistant/components/notify/clicksend_tts.py +++ b/homeassistant/components/clicksend_tts/notify.py @@ -17,7 +17,8 @@ CONF_API_KEY, CONF_RECIPIENT, CONF_USERNAME, CONTENT_TYPE_JSON) import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/command_line.py b/homeassistant/components/command_line/notify.py similarity index 91% rename from homeassistant/components/notify/command_line.py rename to homeassistant/components/command_line/notify.py index 6a81dace28803d..7ea5a6d8880bd3 100644 --- a/homeassistant/components/notify/command_line.py +++ b/homeassistant/components/command_line/notify.py @@ -12,7 +12,8 @@ from homeassistant.const import CONF_COMMAND, CONF_NAME import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/demo.py b/homeassistant/components/demo/notify.py similarity index 92% rename from homeassistant/components/notify/demo.py rename to homeassistant/components/demo/notify.py index 9cb6160901730e..5b8e1f1688f099 100644 --- a/homeassistant/components/notify/demo.py +++ b/homeassistant/components/demo/notify.py @@ -4,7 +4,7 @@ For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from . import BaseNotificationService +from homeassistant.components.notify import BaseNotificationService EVENT_NOTIFY = "notify" diff --git a/homeassistant/components/discord/__init__.py b/homeassistant/components/discord/__init__.py new file mode 100644 index 00000000000000..a3cd87bc895f6c --- /dev/null +++ b/homeassistant/components/discord/__init__.py @@ -0,0 +1 @@ +"""The discord component.""" diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/discord/notify.py similarity index 91% rename from homeassistant/components/notify/discord.py rename to homeassistant/components/discord/notify.py index f98c136b3f975e..d73382d8bcf980 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/discord/notify.py @@ -11,7 +11,9 @@ from homeassistant.const import CONF_TOKEN import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, ATTR_TARGET, + PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/facebook/__init__.py b/homeassistant/components/facebook/__init__.py new file mode 100644 index 00000000000000..1619b8a91f1047 --- /dev/null +++ b/homeassistant/components/facebook/__init__.py @@ -0,0 +1 @@ +"""The facebook component.""" diff --git a/homeassistant/components/notify/facebook.py b/homeassistant/components/facebook/notify.py similarity index 95% rename from homeassistant/components/notify/facebook.py rename to homeassistant/components/facebook/notify.py index b642a7b932b483..2c691661a29d77 100644 --- a/homeassistant/components/notify/facebook.py +++ b/homeassistant/components/facebook/notify.py @@ -14,7 +14,9 @@ from homeassistant.const import CONTENT_TYPE_JSON import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, ATTR_TARGET, + PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/notify/file.py b/homeassistant/components/file/notify.py similarity index 97% rename from homeassistant/components/notify/file.py rename to homeassistant/components/file/notify.py index 98f6da66e3d602..d449476469baea 100644 --- a/homeassistant/components/notify/file.py +++ b/homeassistant/components/file/notify.py @@ -13,7 +13,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) CONF_TIMESTAMP = 'timestamp' diff --git a/homeassistant/components/flock/__init__.py b/homeassistant/components/flock/__init__.py new file mode 100644 index 00000000000000..1b58d21cff8857 --- /dev/null +++ b/homeassistant/components/flock/__init__.py @@ -0,0 +1 @@ +"""The flock component.""" diff --git a/homeassistant/components/notify/flock.py b/homeassistant/components/flock/notify.py similarity index 93% rename from homeassistant/components/notify/flock.py rename to homeassistant/components/flock/notify.py index 16f38bbbb63276..b37483d2f1318a 100644 --- a/homeassistant/components/notify/flock.py +++ b/homeassistant/components/flock/notify.py @@ -14,7 +14,8 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://api.flock.com/hooks/sendMessage/' diff --git a/homeassistant/components/free_mobile/__init__.py b/homeassistant/components/free_mobile/__init__.py new file mode 100644 index 00000000000000..002a1475fde0fa --- /dev/null +++ b/homeassistant/components/free_mobile/__init__.py @@ -0,0 +1 @@ +"""The free_mobile component.""" diff --git a/homeassistant/components/notify/free_mobile.py b/homeassistant/components/free_mobile/notify.py similarity index 92% rename from homeassistant/components/notify/free_mobile.py rename to homeassistant/components/free_mobile/notify.py index fb7829105fce75..1c6804f6d82b98 100644 --- a/homeassistant/components/notify/free_mobile.py +++ b/homeassistant/components/free_mobile/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_ACCESS_TOKEN, CONF_USERNAME import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['freesms==0.1.2'] diff --git a/homeassistant/components/gntp/__init__.py b/homeassistant/components/gntp/__init__.py new file mode 100644 index 00000000000000..c2814f86f06d87 --- /dev/null +++ b/homeassistant/components/gntp/__init__.py @@ -0,0 +1 @@ +"""The gntp component.""" diff --git a/homeassistant/components/notify/gntp.py b/homeassistant/components/gntp/notify.py similarity index 98% rename from homeassistant/components/notify/gntp.py rename to homeassistant/components/gntp/notify.py index 4560eb21f31c7f..915d33668b4e05 100644 --- a/homeassistant/components/notify/gntp.py +++ b/homeassistant/components/gntp/notify.py @@ -12,7 +12,7 @@ from homeassistant.const import CONF_PASSWORD, CONF_PORT import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['gntp==1.0.3'] diff --git a/homeassistant/components/notify/group.py b/homeassistant/components/group/notify.py similarity index 97% rename from homeassistant/components/notify/group.py rename to homeassistant/components/group/notify.py index 85d4d53e3bed3b..200464bb8cb318 100644 --- a/homeassistant/components/notify/group.py +++ b/homeassistant/components/group/notify.py @@ -14,7 +14,7 @@ from homeassistant.const import ATTR_SERVICE import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_MESSAGE, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hipchat/__init__.py b/homeassistant/components/hipchat/__init__.py new file mode 100644 index 00000000000000..8b79982fa43d67 --- /dev/null +++ b/homeassistant/components/hipchat/__init__.py @@ -0,0 +1 @@ +"""The hipchat component.""" diff --git a/homeassistant/components/notify/hipchat.py b/homeassistant/components/hipchat/notify.py similarity index 94% rename from homeassistant/components/notify/hipchat.py rename to homeassistant/components/hipchat/notify.py index 8ce7b8b120e8b6..9e415171f29c4e 100644 --- a/homeassistant/components/notify/hipchat.py +++ b/homeassistant/components/hipchat/notify.py @@ -11,7 +11,9 @@ from homeassistant.const import CONF_HOST, CONF_ROOM, CONF_TOKEN import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, ATTR_TARGET, + PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['hipnotify==1.0.8'] diff --git a/homeassistant/components/html5/__init__.py b/homeassistant/components/html5/__init__.py new file mode 100644 index 00000000000000..88e437ef5666d4 --- /dev/null +++ b/homeassistant/components/html5/__init__.py @@ -0,0 +1 @@ +"""The html5 component.""" diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/html5/notify.py similarity index 99% rename from homeassistant/components/notify/html5.py rename to homeassistant/components/html5/notify.py index 0e99727e81bfb2..8744d0afb283b2 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/html5/notify.py @@ -25,7 +25,7 @@ from homeassistant.util import ensure_unique_string from homeassistant.util.json import load_json, save_json -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/notify/kodi.py b/homeassistant/components/kodi/notify.py similarity index 98% rename from homeassistant/components/notify/kodi.py rename to homeassistant/components/kodi/notify.py index 2dd33bf8990603..7c2a60f349863c 100644 --- a/homeassistant/components/notify/kodi.py +++ b/homeassistant/components/kodi/notify.py @@ -15,7 +15,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/lannouncer/__init__.py b/homeassistant/components/lannouncer/__init__.py new file mode 100644 index 00000000000000..479e9893f84fcb --- /dev/null +++ b/homeassistant/components/lannouncer/__init__.py @@ -0,0 +1 @@ +"""The lannouncer component.""" diff --git a/homeassistant/components/notify/lannouncer.py b/homeassistant/components/lannouncer/notify.py similarity index 94% rename from homeassistant/components/notify/lannouncer.py rename to homeassistant/components/lannouncer/notify.py index 5c975e8422f987..3b2e72b398c9ec 100644 --- a/homeassistant/components/notify/lannouncer.py +++ b/homeassistant/components/lannouncer/notify.py @@ -13,7 +13,8 @@ from homeassistant.const import CONF_HOST, CONF_PORT import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, + BaseNotificationService) ATTR_METHOD = 'method' ATTR_METHOD_DEFAULT = 'speak' diff --git a/homeassistant/components/llamalab_automate/__init__.py b/homeassistant/components/llamalab_automate/__init__.py new file mode 100644 index 00000000000000..f60abfb93c9a9e --- /dev/null +++ b/homeassistant/components/llamalab_automate/__init__.py @@ -0,0 +1 @@ +"""The llamalab_automate component.""" diff --git a/homeassistant/components/notify/llamalab_automate.py b/homeassistant/components/llamalab_automate/notify.py similarity index 93% rename from homeassistant/components/notify/llamalab_automate.py rename to homeassistant/components/llamalab_automate/notify.py index d3689dbbd818e9..6b59d11e0a3fc1 100644 --- a/homeassistant/components/notify/llamalab_automate.py +++ b/homeassistant/components/llamalab_automate/notify.py @@ -12,7 +12,8 @@ from homeassistant.const import CONF_API_KEY, CONF_DEVICE from homeassistant.helpers import config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) _RESOURCE = 'https://llamalab.com/automate/cloud/message' diff --git a/homeassistant/components/mastodon/__init__.py b/homeassistant/components/mastodon/__init__.py new file mode 100644 index 00000000000000..123d23afb801a5 --- /dev/null +++ b/homeassistant/components/mastodon/__init__.py @@ -0,0 +1 @@ +"""The mastodon component.""" diff --git a/homeassistant/components/notify/mastodon.py b/homeassistant/components/mastodon/notify.py similarity index 93% rename from homeassistant/components/notify/mastodon.py rename to homeassistant/components/mastodon/notify.py index 59c787bc026bc3..6192f6cdca5e30 100644 --- a/homeassistant/components/notify/mastodon.py +++ b/homeassistant/components/mastodon/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_ACCESS_TOKEN import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['Mastodon.py==1.3.1'] diff --git a/homeassistant/components/message_bird/__init__.py b/homeassistant/components/message_bird/__init__.py new file mode 100644 index 00000000000000..ed3828c5edad91 --- /dev/null +++ b/homeassistant/components/message_bird/__init__.py @@ -0,0 +1 @@ +"""The message_bird component.""" diff --git a/homeassistant/components/notify/message_bird.py b/homeassistant/components/message_bird/notify.py similarity index 93% rename from homeassistant/components/notify/message_bird.py rename to homeassistant/components/message_bird/notify.py index c45d153d813a0e..cfb22ff1d5235b 100644 --- a/homeassistant/components/notify/message_bird.py +++ b/homeassistant/components/message_bird/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_API_KEY, CONF_SENDER import homeassistant.helpers.config_validation as cv -from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['messagebird==1.2.0'] diff --git a/homeassistant/components/notify/mycroft.py b/homeassistant/components/mycroft/notify.py similarity index 93% rename from homeassistant/components/notify/mycroft.py rename to homeassistant/components/mycroft/notify.py index e9cd44d5d06b07..a8a401a9c1f054 100644 --- a/homeassistant/components/notify/mycroft.py +++ b/homeassistant/components/mycroft/notify.py @@ -6,7 +6,7 @@ """ import logging -from . import BaseNotificationService +from homeassistant.components.notify import BaseNotificationService DEPENDENCIES = ['mycroft'] diff --git a/homeassistant/components/nfandroidtv/__init__.py b/homeassistant/components/nfandroidtv/__init__.py new file mode 100644 index 00000000000000..9965265e00d658 --- /dev/null +++ b/homeassistant/components/nfandroidtv/__init__.py @@ -0,0 +1 @@ +"""The nfandroidtv component.""" diff --git a/homeassistant/components/notify/nfandroidtv.py b/homeassistant/components/nfandroidtv/notify.py similarity index 99% rename from homeassistant/components/notify/nfandroidtv.py rename to homeassistant/components/nfandroidtv/notify.py index 4d39083387c1ed..c4003a6312a523 100644 --- a/homeassistant/components/notify/nfandroidtv.py +++ b/homeassistant/components/nfandroidtv/notify.py @@ -15,7 +15,7 @@ from homeassistant.const import CONF_TIMEOUT, CONF_HOST import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/prowl/__init__.py b/homeassistant/components/prowl/__init__.py new file mode 100644 index 00000000000000..1cf58a25120925 --- /dev/null +++ b/homeassistant/components/prowl/__init__.py @@ -0,0 +1 @@ +"""The prowl component.""" diff --git a/homeassistant/components/notify/prowl.py b/homeassistant/components/prowl/notify.py similarity index 98% rename from homeassistant/components/notify/prowl.py rename to homeassistant/components/prowl/notify.py index 27ce8d0fb7a7ce..6d911789121ba1 100644 --- a/homeassistant/components/notify/prowl.py +++ b/homeassistant/components/prowl/notify.py @@ -14,7 +14,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/pushbullet/notify.py similarity index 99% rename from homeassistant/components/notify/pushbullet.py rename to homeassistant/components/pushbullet/notify.py index 505d6f4e1c144f..f0b4ec24da8a4b 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/pushbullet/notify.py @@ -12,7 +12,7 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/pushetta/__init__.py b/homeassistant/components/pushetta/__init__.py new file mode 100644 index 00000000000000..f992fecddb73e4 --- /dev/null +++ b/homeassistant/components/pushetta/__init__.py @@ -0,0 +1 @@ +"""The pushetta component.""" diff --git a/homeassistant/components/notify/pushetta.py b/homeassistant/components/pushetta/notify.py similarity index 98% rename from homeassistant/components/notify/pushetta.py rename to homeassistant/components/pushetta/notify.py index 5db67177548686..106c0641a699a2 100644 --- a/homeassistant/components/notify/pushetta.py +++ b/homeassistant/components/pushetta/notify.py @@ -11,7 +11,7 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/pushover/__init__.py b/homeassistant/components/pushover/__init__.py new file mode 100644 index 00000000000000..921d37ed332975 --- /dev/null +++ b/homeassistant/components/pushover/__init__.py @@ -0,0 +1 @@ +"""The pushover component.""" diff --git a/homeassistant/components/notify/pushover.py b/homeassistant/components/pushover/notify.py similarity index 97% rename from homeassistant/components/notify/pushover.py rename to homeassistant/components/pushover/notify.py index 372ea361f13bde..78e9ed11c956e2 100644 --- a/homeassistant/components/notify/pushover.py +++ b/homeassistant/components/pushover/notify.py @@ -11,7 +11,7 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/pushsafer/__init__.py b/homeassistant/components/pushsafer/__init__.py new file mode 100644 index 00000000000000..81dfc7e15fd11b --- /dev/null +++ b/homeassistant/components/pushsafer/__init__.py @@ -0,0 +1 @@ +"""The pushsafer component.""" diff --git a/homeassistant/components/notify/pushsafer.py b/homeassistant/components/pushsafer/notify.py similarity index 99% rename from homeassistant/components/notify/pushsafer.py rename to homeassistant/components/pushsafer/notify.py index 9a3308ff21e002..a1fa2b7409c6e7 100644 --- a/homeassistant/components/notify/pushsafer.py +++ b/homeassistant/components/pushsafer/notify.py @@ -14,7 +14,7 @@ import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/notify/rest.py b/homeassistant/components/rest/notify.py similarity index 99% rename from homeassistant/components/notify/rest.py rename to homeassistant/components/rest/notify.py index eec2ea4aa37bcb..de75db83848d90 100644 --- a/homeassistant/components/notify/rest.py +++ b/homeassistant/components/rest/notify.py @@ -15,7 +15,7 @@ HTTP_DIGEST_AUTHENTICATION) import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/rocketchat/__init__.py b/homeassistant/components/rocketchat/__init__.py new file mode 100644 index 00000000000000..1d2c281f66149d --- /dev/null +++ b/homeassistant/components/rocketchat/__init__.py @@ -0,0 +1 @@ +"""The rocketchat component.""" diff --git a/homeassistant/components/notify/rocketchat.py b/homeassistant/components/rocketchat/notify.py similarity index 94% rename from homeassistant/components/notify/rocketchat.py rename to homeassistant/components/rocketchat/notify.py index 116c32993d829b..8bf1e172264aa4 100644 --- a/homeassistant/components/notify/rocketchat.py +++ b/homeassistant/components/rocketchat/notify.py @@ -12,7 +12,8 @@ CONF_PASSWORD, CONF_ROOM, CONF_URL, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['rocketchat-API==0.6.1'] diff --git a/homeassistant/components/sendgrid/__init__.py b/homeassistant/components/sendgrid/__init__.py new file mode 100644 index 00000000000000..91fff97c150f76 --- /dev/null +++ b/homeassistant/components/sendgrid/__init__.py @@ -0,0 +1 @@ +"""The sendgrid component.""" diff --git a/homeassistant/components/notify/sendgrid.py b/homeassistant/components/sendgrid/notify.py similarity index 98% rename from homeassistant/components/notify/sendgrid.py rename to homeassistant/components/sendgrid/notify.py index 6bab566bfc7e69..211e288725e9ab 100644 --- a/homeassistant/components/notify/sendgrid.py +++ b/homeassistant/components/sendgrid/notify.py @@ -12,7 +12,7 @@ CONF_API_KEY, CONF_RECIPIENT, CONF_SENDER, CONTENT_TYPE_TEXT_PLAIN) import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['sendgrid==5.6.0'] diff --git a/homeassistant/components/simplepush/__init__.py b/homeassistant/components/simplepush/__init__.py new file mode 100644 index 00000000000000..8253cfad8b4655 --- /dev/null +++ b/homeassistant/components/simplepush/__init__.py @@ -0,0 +1 @@ +"""The simplepush component.""" diff --git a/homeassistant/components/notify/simplepush.py b/homeassistant/components/simplepush/notify.py similarity index 97% rename from homeassistant/components/notify/simplepush.py rename to homeassistant/components/simplepush/notify.py index 1d198b5adec75d..63222d4adc1f52 100644 --- a/homeassistant/components/notify/simplepush.py +++ b/homeassistant/components/simplepush/notify.py @@ -11,7 +11,7 @@ from homeassistant.const import CONF_PASSWORD import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['simplepush==1.1.4'] diff --git a/homeassistant/components/slack/__init__.py b/homeassistant/components/slack/__init__.py new file mode 100644 index 00000000000000..a999c2375ff060 --- /dev/null +++ b/homeassistant/components/slack/__init__.py @@ -0,0 +1 @@ +"""The slack component.""" diff --git a/homeassistant/components/notify/slack.py b/homeassistant/components/slack/notify.py similarity index 99% rename from homeassistant/components/notify/slack.py rename to homeassistant/components/slack/notify.py index 7aee58ed012e20..eabddf012993db 100644 --- a/homeassistant/components/notify/slack.py +++ b/homeassistant/components/slack/notify.py @@ -13,7 +13,7 @@ from homeassistant.const import CONF_API_KEY, CONF_ICON, CONF_USERNAME import homeassistant.helpers.config_validation as cv -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/smtp/__init__.py b/homeassistant/components/smtp/__init__.py new file mode 100644 index 00000000000000..5e7fb41c2127a1 --- /dev/null +++ b/homeassistant/components/smtp/__init__.py @@ -0,0 +1 @@ +"""The smtp component.""" diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/smtp/notify.py similarity index 99% rename from homeassistant/components/notify/smtp.py rename to homeassistant/components/smtp/notify.py index 995aae76cc6d2f..4104013bcf7236 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/smtp/notify.py @@ -21,7 +21,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/stride/__init__.py b/homeassistant/components/stride/__init__.py new file mode 100644 index 00000000000000..461a3ee744f9bc --- /dev/null +++ b/homeassistant/components/stride/__init__.py @@ -0,0 +1 @@ +"""The stride component.""" diff --git a/homeassistant/components/notify/stride.py b/homeassistant/components/stride/notify.py similarity index 93% rename from homeassistant/components/notify/stride.py rename to homeassistant/components/stride/notify.py index f5f5b52ab67c4c..9d05bd17f34c51 100644 --- a/homeassistant/components/notify/stride.py +++ b/homeassistant/components/stride/notify.py @@ -11,7 +11,9 @@ from homeassistant.const import CONF_ROOM, CONF_TOKEN import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, ATTR_TARGET, + PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['pystride==0.1.7'] diff --git a/homeassistant/components/synology_chat/__init__.py b/homeassistant/components/synology_chat/__init__.py new file mode 100644 index 00000000000000..836eff6ee48b16 --- /dev/null +++ b/homeassistant/components/synology_chat/__init__.py @@ -0,0 +1 @@ +"""The synology_chat component.""" diff --git a/homeassistant/components/notify/synology_chat.py b/homeassistant/components/synology_chat/notify.py similarity index 92% rename from homeassistant/components/notify/synology_chat.py rename to homeassistant/components/synology_chat/notify.py index 023586ae532983..32277dc1971864 100644 --- a/homeassistant/components/notify/synology_chat.py +++ b/homeassistant/components/synology_chat/notify.py @@ -13,7 +13,8 @@ from homeassistant.const import CONF_RESOURCE, CONF_VERIFY_SSL import homeassistant.helpers.config_validation as cv -from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, + BaseNotificationService) ATTR_FILE_URL = 'file_url' diff --git a/homeassistant/components/syslog/__init__.py b/homeassistant/components/syslog/__init__.py new file mode 100644 index 00000000000000..c46e56e76ffb27 --- /dev/null +++ b/homeassistant/components/syslog/__init__.py @@ -0,0 +1 @@ +"""The syslog component.""" diff --git a/homeassistant/components/notify/syslog.py b/homeassistant/components/syslog/notify.py similarity index 98% rename from homeassistant/components/notify/syslog.py rename to homeassistant/components/syslog/notify.py index 4f7900b8d45c9a..740148e28e505c 100644 --- a/homeassistant/components/notify/syslog.py +++ b/homeassistant/components/syslog/notify.py @@ -8,7 +8,7 @@ import voluptuous as vol -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/telegram/__init__.py b/homeassistant/components/telegram/__init__.py new file mode 100644 index 00000000000000..1aca4e510c6427 --- /dev/null +++ b/homeassistant/components/telegram/__init__.py @@ -0,0 +1 @@ +"""The telegram component.""" diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/telegram/notify.py similarity index 98% rename from homeassistant/components/notify/telegram.py rename to homeassistant/components/telegram/notify.py index a6975f60fae299..428c7e093d2365 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/telegram/notify.py @@ -10,7 +10,7 @@ from homeassistant.const import ATTR_LOCATION -from . import ( +from homeassistant.components.notify import ( ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService) diff --git a/homeassistant/components/twilio_call/__init__.py b/homeassistant/components/twilio_call/__init__.py new file mode 100644 index 00000000000000..87b225b713a6b7 --- /dev/null +++ b/homeassistant/components/twilio_call/__init__.py @@ -0,0 +1 @@ +"""The twilio_call component.""" diff --git a/homeassistant/components/notify/twilio_call.py b/homeassistant/components/twilio_call/notify.py similarity index 92% rename from homeassistant/components/notify/twilio_call.py rename to homeassistant/components/twilio_call/notify.py index 4826ab776121fd..a1a28a03b18c5d 100644 --- a/homeassistant/components/notify/twilio_call.py +++ b/homeassistant/components/twilio_call/notify.py @@ -12,7 +12,8 @@ from homeassistant.components.twilio import DATA_TWILIO import homeassistant.helpers.config_validation as cv -from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/twilio_sms/__init__.py b/homeassistant/components/twilio_sms/__init__.py new file mode 100644 index 00000000000000..3bf3898ac3f634 --- /dev/null +++ b/homeassistant/components/twilio_sms/__init__.py @@ -0,0 +1 @@ +"""The twilio_sms component.""" diff --git a/homeassistant/components/notify/twilio_sms.py b/homeassistant/components/twilio_sms/notify.py similarity index 91% rename from homeassistant/components/notify/twilio_sms.py rename to homeassistant/components/twilio_sms/notify.py index 165e743977d276..b3b35ea17899b9 100644 --- a/homeassistant/components/notify/twilio_sms.py +++ b/homeassistant/components/twilio_sms/notify.py @@ -11,7 +11,8 @@ from homeassistant.components.twilio import DATA_TWILIO import homeassistant.helpers.config_validation as cv -from . import ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, + BaseNotificationService) _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ["twilio"] diff --git a/homeassistant/components/twitter/__init__.py b/homeassistant/components/twitter/__init__.py new file mode 100644 index 00000000000000..1ecba66a44e0fc --- /dev/null +++ b/homeassistant/components/twitter/__init__.py @@ -0,0 +1 @@ +"""The twitter component.""" diff --git a/homeassistant/components/notify/twitter.py b/homeassistant/components/twitter/notify.py similarity index 98% rename from homeassistant/components/notify/twitter.py rename to homeassistant/components/twitter/notify.py index 43d977d26a63e5..9172da367856df 100644 --- a/homeassistant/components/notify/twitter.py +++ b/homeassistant/components/twitter/notify.py @@ -17,7 +17,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_point_in_time -from . import ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['TwitterAPI==2.5.9'] diff --git a/homeassistant/components/xmpp/__init__.py b/homeassistant/components/xmpp/__init__.py new file mode 100644 index 00000000000000..40736a4fd363ae --- /dev/null +++ b/homeassistant/components/xmpp/__init__.py @@ -0,0 +1 @@ +"""The xmpp component.""" diff --git a/homeassistant/components/notify/xmpp.py b/homeassistant/components/xmpp/notify.py similarity index 99% rename from homeassistant/components/notify/xmpp.py rename to homeassistant/components/xmpp/notify.py index 3827674316b66a..5a14046bd41a3f 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/xmpp/notify.py @@ -19,7 +19,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.helpers.template as template_helper -from . import ( +from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) REQUIREMENTS = ['slixmpp==1.4.2'] diff --git a/homeassistant/components/yessssms/__init__.py b/homeassistant/components/yessssms/__init__.py new file mode 100644 index 00000000000000..bc5f422ba75b48 --- /dev/null +++ b/homeassistant/components/yessssms/__init__.py @@ -0,0 +1 @@ +"""The yessssms component.""" diff --git a/homeassistant/components/notify/yessssms.py b/homeassistant/components/yessssms/notify.py similarity index 95% rename from homeassistant/components/notify/yessssms.py rename to homeassistant/components/yessssms/notify.py index 19efa2a53d8b34..529aa4e7b6e6b7 100644 --- a/homeassistant/components/notify/yessssms.py +++ b/homeassistant/components/yessssms/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_PASSWORD, CONF_RECIPIENT, CONF_USERNAME import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, BaseNotificationService +from homeassistant.components.notify import (PLATFORM_SCHEMA, + BaseNotificationService) REQUIREMENTS = ['YesssSMS==0.2.3'] diff --git a/requirements_all.txt b/requirements_all.txt index 664e725db6133d..4f666d6e215448 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -35,7 +35,7 @@ Adafruit-SHT31==1.0.2 # homeassistant.components.homekit HAP-python==2.4.2 -# homeassistant.components.notify.mastodon +# homeassistant.components.mastodon.notify Mastodon.py==1.3.1 # homeassistant.components.github.sensor @@ -78,7 +78,7 @@ RtmAPI==0.7.0 # homeassistant.components.travisci.sensor TravisPy==0.3.5 -# homeassistant.components.notify.twitter +# homeassistant.components.twitter.notify TwitterAPI==2.5.9 # homeassistant.components.tof.sensor @@ -87,7 +87,7 @@ TwitterAPI==2.5.9 # homeassistant.components.waze_travel_time.sensor WazeRouteCalculator==0.9 -# homeassistant.components.notify.yessssms +# homeassistant.components.yessssms.notify YesssSMS==0.2.3 # homeassistant.components.abode @@ -169,7 +169,7 @@ anthemav==1.1.10 # homeassistant.components.apcupsd apcaccess==0.0.13 -# homeassistant.components.notify.apns +# homeassistant.components.apns.notify apns2==0.3.0 # homeassistant.components.aqualogic @@ -230,9 +230,9 @@ blockchain==1.4.4 # bme680==1.0.5 # homeassistant.components.route53 -# homeassistant.components.notify.aws_lambda -# homeassistant.components.notify.aws_sns -# homeassistant.components.notify.aws_sqs +# homeassistant.components.aws_lambda.notify +# homeassistant.components.aws_sns.notify +# homeassistant.components.aws_sqs.notify boto3==1.9.16 # homeassistant.scripts.credstash @@ -270,7 +270,7 @@ caldav==0.5.0 # homeassistant.components.cisco_mobility_express.device_tracker ciscomobilityexpress==0.1.5 -# homeassistant.components.notify.ciscospark +# homeassistant.components.ciscospark.notify ciscosparkapi==0.4.2 # homeassistant.components.cppm_tracker.device_tracker @@ -341,7 +341,7 @@ directpy==0.5 # homeassistant.components.discogs.sensor discogs_client==2.2.1 -# homeassistant.components.notify.discord +# homeassistant.components.discord.notify discord.py==0.16.12 # homeassistant.components.updater @@ -443,7 +443,7 @@ flux_led==0.22 # homeassistant.components.foobot.sensor foobot_async==0.3.1 -# homeassistant.components.notify.free_mobile +# homeassistant.components.free_mobile.notify freesms==0.1.2 # homeassistant.components.fritz.device_tracker @@ -477,7 +477,7 @@ gitterpy==0.1.7 # homeassistant.components.glances.sensor glances_api==0.2.0 -# homeassistant.components.notify.gntp +# homeassistant.components.gntp.notify gntp==1.0.3 # homeassistant.components.google @@ -531,7 +531,7 @@ heatmiserV3==0.9.1 # homeassistant.components.hikvisioncam.switch hikvision==0.4 -# homeassistant.components.notify.hipchat +# homeassistant.components.hipchat.notify hipnotify==1.0.8 # homeassistant.components.harman_kardon_avr.media_player @@ -602,7 +602,7 @@ ipify==1.0.0 jsonpath==0.75 # homeassistant.components.kodi.media_player -# homeassistant.components.notify.kodi +# homeassistant.components.kodi.notify jsonrpc-async==0.6 # homeassistant.components.kodi.media_player @@ -690,7 +690,7 @@ maxcube-api==0.1.0 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 -# homeassistant.components.notify.message_bird +# homeassistant.components.message_bird.notify messagebird==1.2.0 # homeassistant.components.meteo_france @@ -876,11 +876,11 @@ psutil==5.6.1 # homeassistant.components.wink pubnubsub-handler==1.0.3 -# homeassistant.components.notify.pushbullet +# homeassistant.components.pushbullet.notify # homeassistant.components.pushbullet.sensor pushbullet.py==0.11.0 -# homeassistant.components.notify.pushetta +# homeassistant.components.pushetta.notify pushetta==1.0.15 # homeassistant.components.rpi_gpio_pwm.light @@ -1283,7 +1283,7 @@ pysonos==0.0.8 # homeassistant.components.spc pyspcwebgw==0.4.0 -# homeassistant.components.notify.stride +# homeassistant.components.stride.notify pystride==0.1.7 # homeassistant.components.syncthru.sensor @@ -1338,7 +1338,7 @@ python-hpilo==3.9 # homeassistant.components.joaoapps_join # homeassistant.components.joaoapps_join.notify -python-join-api==0.0.2 +python-join-api==0.0.4 # homeassistant.components.juicenet python-juicenet==0.0.5 @@ -1368,7 +1368,7 @@ python-nest==4.1.0 # homeassistant.components.nmap_tracker.device_tracker python-nmap==0.6.1 -# homeassistant.components.notify.pushover +# homeassistant.components.pushover.notify python-pushover==0.3 # homeassistant.components.qbittorrent.sensor @@ -1461,7 +1461,7 @@ pyvizio==0.0.4 # homeassistant.components.velux pyvlx==0.2.10 -# homeassistant.components.notify.html5 +# homeassistant.components.html5.notify pywebpush==1.6.0 # homeassistant.components.wemo @@ -1521,7 +1521,7 @@ ritassist==0.9.2 # homeassistant.components.rejseplanen.sensor rjpl==0.3.5 -# homeassistant.components.notify.rocketchat +# homeassistant.components.rocketchat.notify rocketchat-API==0.6.1 # homeassistant.components.roomba.vacuum @@ -1554,7 +1554,7 @@ schiene==0.23 # homeassistant.components.scsgate scsgate==0.1.0 -# homeassistant.components.notify.sendgrid +# homeassistant.components.sendgrid.notify sendgrid==5.6.0 # homeassistant.components.sensehat.light @@ -1570,7 +1570,7 @@ sharp_aquos_rc==0.3.2 # homeassistant.components.shodan.sensor shodan==1.11.1 -# homeassistant.components.notify.simplepush +# homeassistant.components.simplepush.notify simplepush==1.1.4 # homeassistant.components.simplisafe @@ -1582,13 +1582,13 @@ sisyphus-control==2.1 # homeassistant.components.skybell skybellpy==0.3.0 -# homeassistant.components.notify.slack +# homeassistant.components.slack.notify slacker==0.12.0 # homeassistant.components.sleepiq sleepyq==0.6 -# homeassistant.components.notify.xmpp +# homeassistant.components.xmpp.notify slixmpp==1.4.2 # homeassistant.components.smappee diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7721c236d995e2..3cca2d4bf5e69c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -31,7 +31,7 @@ PyRMVtransport==0.1.3 # homeassistant.components.transport_nsw.sensor PyTransportNSW==0.1.1 -# homeassistant.components.notify.yessssms +# homeassistant.components.yessssms.notify YesssSMS==0.2.3 # homeassistant.components.ambient_station @@ -53,7 +53,7 @@ aiohue==1.9.1 # homeassistant.components.unifi aiounifi==4 -# homeassistant.components.notify.apns +# homeassistant.components.apns.notify apns2==0.3.0 # homeassistant.components.stream @@ -186,7 +186,7 @@ pmsensor==0.4 # homeassistant.components.prometheus prometheus_client==0.2.0 -# homeassistant.components.notify.pushbullet +# homeassistant.components.pushbullet.notify # homeassistant.components.pushbullet.sensor pushbullet.py==0.11.0 @@ -260,7 +260,7 @@ pytradfri[async]==6.0.1 # homeassistant.components.unifi.device_tracker pyunifi==2.16 -# homeassistant.components.notify.html5 +# homeassistant.components.html5.notify pywebpush==1.6.0 # homeassistant.components.rainmachine diff --git a/tests/components/apns/__init__.py b/tests/components/apns/__init__.py new file mode 100644 index 00000000000000..42c980a62a7d1d --- /dev/null +++ b/tests/components/apns/__init__.py @@ -0,0 +1 @@ +"""Tests for the apns component.""" diff --git a/tests/components/notify/test_apns.py b/tests/components/apns/test_notify.py similarity index 92% rename from tests/components/notify/test_apns.py rename to tests/components/apns/test_notify.py index 9964a58cd2416b..7303f4872e3a04 100644 --- a/tests/components/notify/test_apns.py +++ b/tests/components/apns/test_notify.py @@ -8,7 +8,7 @@ import homeassistant.components.notify as notify from homeassistant.setup import setup_component -from homeassistant.components.notify import apns +import homeassistant.components.apns.notify as apns from homeassistant.core import State from tests.common import assert_setup_component, get_test_home_assistant @@ -23,7 +23,7 @@ } -@patch('homeassistant.components.notify.apns.open', mock_open(), create=True) +@patch('homeassistant.components.apns.notify.open', mock_open(), create=True) class TestApns(unittest.TestCase): """Test the APNS component.""" @@ -102,7 +102,7 @@ def test_apns_setup_missing_topic(self): assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] - @patch('homeassistant.components.notify.apns._write_device') + @patch('homeassistant.components.apns.notify._write_device') def test_register_new_device(self, mock_write): """Test registering a new device with a name.""" yaml_file = {5678: {'name': 'test device 2'}} @@ -116,7 +116,7 @@ def fake_write(_out, device): mock_write.side_effect = fake_write with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -128,7 +128,7 @@ def fake_write(_out, device): assert len(written_devices) == 1 assert written_devices[0].name == 'test device' - @patch('homeassistant.components.notify.apns._write_device') + @patch('homeassistant.components.apns.notify._write_device') def test_register_device_without_name(self, mock_write): """Test registering a without a name.""" yaml_file = { @@ -151,7 +151,7 @@ def fake_write(_out, device): mock_write.side_effect = fake_write with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -166,7 +166,7 @@ def fake_write(_out, device): assert test_device is not None assert test_device.name is None - @patch('homeassistant.components.notify.apns._write_device') + @patch('homeassistant.components.apns.notify._write_device') def test_update_existing_device(self, mock_write): """Test updating an existing device.""" yaml_file = { @@ -187,7 +187,7 @@ def fake_write(_out, device): mock_write.side_effect = fake_write with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -206,7 +206,7 @@ def fake_write(_out, device): assert 'updated device 1' == test_device_1.name - @patch('homeassistant.components.notify.apns._write_device') + @patch('homeassistant.components.apns.notify._write_device') def test_update_existing_device_with_tracking_id(self, mock_write): """Test updating an existing device that has a tracking id.""" yaml_file = { @@ -229,7 +229,7 @@ def fake_write(_out, device): mock_write.side_effect = fake_write with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -259,7 +259,7 @@ def test_send(self, mock_client): yaml_file = {1234: {'name': 'test device 1'}} with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -294,7 +294,7 @@ def test_send_when_disabled(self, mock_client): }} with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() @@ -325,7 +325,7 @@ def test_send_with_state(self, mock_client): } with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)), \ patch('os.path.isfile', Mock(return_value=True)): notify_service = apns.ApnsNotificationService( @@ -353,7 +353,7 @@ def test_send_with_state(self, mock_client): assert 'Hello' == payload.alert @patch('apns2.client.APNsClient') - @patch('homeassistant.components.notify.apns._write_device') + @patch('homeassistant.components.apns.notify._write_device') def test_disable_when_unregistered(self, mock_write, mock_client): """Test disabling a device when it is unregistered.""" send = mock_client.return_value.send_notification @@ -379,7 +379,7 @@ def fake_write(_out, device): mock_write.side_effect = fake_write with patch( - 'homeassistant.components.notify.apns.load_yaml_config_file', + 'homeassistant.components.apns.notify.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() diff --git a/tests/components/notify/test_command_line.py b/tests/components/command_line/test_notify.py similarity index 97% rename from tests/components/notify/test_command_line.py rename to tests/components/command_line/test_notify.py index 66aa451d3894cf..522519f2ccebd3 100644 --- a/tests/components/notify/test_command_line.py +++ b/tests/components/command_line/test_notify.py @@ -65,7 +65,7 @@ def test_command_line_output(self): # the echo command adds a line break assert fil.read() == "{}\n".format(message) - @patch('homeassistant.components.notify.command_line._LOGGER.error') + @patch('homeassistant.components.command_line.notify._LOGGER.error') def test_error_for_none_zero_exit_code(self, mock_error): """Test if an error is logged for non zero exit codes.""" with assert_setup_component(1) as handle_config: diff --git a/tests/components/notify/test_demo.py b/tests/components/demo/test_notify.py similarity index 97% rename from tests/components/notify/test_demo.py rename to tests/components/demo/test_notify.py index 4c3f3bf3f73ef6..35cf8abe6bd391 100644 --- a/tests/components/notify/test_demo.py +++ b/tests/components/demo/test_notify.py @@ -7,7 +7,7 @@ import homeassistant.components.notify as notify from homeassistant.setup import setup_component -from homeassistant.components.notify import demo +import homeassistant.components.demo.notify as demo from homeassistant.core import callback from homeassistant.helpers import discovery, script @@ -50,7 +50,7 @@ def test_setup(self): """Test setup.""" self._setup_notify() - @patch('homeassistant.components.notify.demo.get_service', autospec=True) + @patch('homeassistant.components.demo.notify.get_service', autospec=True) def test_no_notify_service(self, mock_demo_get_service): """Test missing platform notify service instance.""" mock_demo_get_service.return_value = None @@ -63,7 +63,7 @@ def test_no_notify_service(self, mock_demo_get_service): ['ERROR:homeassistant.components.notify:' 'Failed to initialize notification service demo'] - @patch('homeassistant.components.notify.demo.get_service', autospec=True) + @patch('homeassistant.components.demo.notify.get_service', autospec=True) def test_discover_notify(self, mock_demo_get_service): """Test discovery of notify demo platform.""" assert notify.DOMAIN not in self.hass.config.components diff --git a/tests/components/facebook/__init__.py b/tests/components/facebook/__init__.py new file mode 100644 index 00000000000000..5f52b1f7614d82 --- /dev/null +++ b/tests/components/facebook/__init__.py @@ -0,0 +1 @@ +"""Tests for the facebook component.""" diff --git a/tests/components/notify/test_facebook.py b/tests/components/facebook/test_notify.py similarity index 97% rename from tests/components/notify/test_facebook.py rename to tests/components/facebook/test_notify.py index a74395d5a5e8a7..ae4698c562fb5c 100644 --- a/tests/components/notify/test_facebook.py +++ b/tests/components/facebook/test_notify.py @@ -2,7 +2,8 @@ import unittest import requests_mock -import homeassistant.components.notify.facebook as facebook +# import homeassistant.components.facebook as facebook +import homeassistant.components.facebook.notify as facebook class TestFacebook(unittest.TestCase): diff --git a/tests/components/notify/test_file.py b/tests/components/file/test_notify.py similarity index 96% rename from tests/components/notify/test_file.py rename to tests/components/file/test_notify.py index e67ed53260423b..b6ed6e26aa19c3 100644 --- a/tests/components/notify/test_file.py +++ b/tests/components/file/test_notify.py @@ -52,9 +52,9 @@ def _test_notify_file(self, timestamp): m_open = mock_open() with patch( - 'homeassistant.components.notify.file.open', + 'homeassistant.components.file.notify.open', m_open, create=True - ), patch('homeassistant.components.notify.file.os.stat') as mock_st, \ + ), patch('homeassistant.components.file.notify.os.stat') as mock_st, \ patch('homeassistant.util.dt.utcnow', return_value=dt_util.utcnow()): diff --git a/tests/components/notify/test_group.py b/tests/components/group/test_notify.py similarity index 96% rename from tests/components/notify/test_group.py rename to tests/components/group/test_notify.py index bbd7c11ffebd65..9412e9f95a4b07 100644 --- a/tests/components/notify/test_group.py +++ b/tests/components/group/test_notify.py @@ -4,7 +4,8 @@ from homeassistant.setup import setup_component import homeassistant.components.notify as notify -from homeassistant.components.notify import group, demo +import homeassistant.components.group.notify as group +import homeassistant.components.demo.notify as demo from homeassistant.util.async_ import run_coroutine_threadsafe from tests.common import assert_setup_component, get_test_home_assistant diff --git a/tests/components/homematic/__init__.py b/tests/components/homematic/__init__.py new file mode 100644 index 00000000000000..9a021f82a7f539 --- /dev/null +++ b/tests/components/homematic/__init__.py @@ -0,0 +1 @@ +"""Tests for the homematic component.""" diff --git a/tests/components/notify/test_homematic.py b/tests/components/homematic/test_notify.py similarity index 100% rename from tests/components/notify/test_homematic.py rename to tests/components/homematic/test_notify.py diff --git a/tests/components/html5/__init__.py b/tests/components/html5/__init__.py new file mode 100644 index 00000000000000..48106286f2e27e --- /dev/null +++ b/tests/components/html5/__init__.py @@ -0,0 +1 @@ +"""Tests for the html5 component.""" diff --git a/tests/components/notify/test_html5.py b/tests/components/html5/test_notify.py similarity index 94% rename from tests/components/notify/test_html5.py rename to tests/components/html5/test_notify.py index e33c297b166ac3..140544bf9ea068 100644 --- a/tests/components/notify/test_html5.py +++ b/tests/components/html5/test_notify.py @@ -5,7 +5,7 @@ from homeassistant.setup import async_setup_component from homeassistant.exceptions import HomeAssistantError -from homeassistant.components.notify import html5 +import homeassistant.components.html5.notify as html5 CONFIG_FILE = 'file.conf' @@ -54,7 +54,7 @@ async def mock_client(hass, hass_client, registrations=None): if registrations is None: registrations = {} - with patch('homeassistant.components.notify.html5._load_config', + with patch('homeassistant.components.html5.notify._load_config', return_value=registrations): await async_setup_component(hass, 'notify', { 'notify': { @@ -189,7 +189,7 @@ async def test_registering_new_device_view(hass, hass_client): """Test that the HTML view works.""" client = await mock_client(hass, hass_client) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1)) assert resp.status == 200 @@ -206,7 +206,7 @@ async def test_registering_new_device_view_with_name(hass, hass_client): SUB_WITH_NAME = SUBSCRIPTION_1.copy() SUB_WITH_NAME['name'] = 'test device' - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: resp = await client.post(REGISTER_URL, data=json.dumps(SUB_WITH_NAME)) assert resp.status == 200 @@ -220,7 +220,7 @@ async def test_registering_new_device_expiration_view(hass, hass_client): """Test that the HTML view works.""" client = await mock_client(hass, hass_client) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4)) assert resp.status == 200 @@ -234,7 +234,7 @@ async def test_registering_new_device_fails_view(hass, hass_client): registrations = {} client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json', + with patch('homeassistant.components.html5.notify.save_json', side_effect=HomeAssistantError()): resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4)) @@ -247,7 +247,7 @@ async def test_registering_existing_device_view(hass, hass_client): registrations = {} client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1)) resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4)) @@ -268,7 +268,7 @@ async def test_registering_existing_device_view_with_name(hass, hass_client): SUB_WITH_NAME = SUBSCRIPTION_1.copy() SUB_WITH_NAME['name'] = 'test device' - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: await client.post(REGISTER_URL, data=json.dumps(SUB_WITH_NAME)) resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4)) @@ -286,7 +286,7 @@ async def test_registering_existing_device_fails_view(hass, hass_client): registrations = {} client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1)) mock_save.side_effect = HomeAssistantError resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4)) @@ -312,7 +312,7 @@ async def test_registering_new_device_validation(hass, hass_client): })) assert resp.status == 400 - with patch('homeassistant.components.notify.html5.save_json', + with patch('homeassistant.components.html5.notify.save_json', return_value=False): resp = await client.post(REGISTER_URL, data=json.dumps({ 'browser': 'chrome', @@ -329,7 +329,7 @@ async def test_unregistering_device_view(hass, hass_client): } client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: resp = await client.delete(REGISTER_URL, data=json.dumps({ 'subscription': SUBSCRIPTION_1['subscription'], })) @@ -347,7 +347,7 @@ async def test_unregister_device_view_handle_unknown_subscription( registrations = {} client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json') as mock_save: + with patch('homeassistant.components.html5.notify.save_json') as mock_save: resp = await client.delete(REGISTER_URL, data=json.dumps({ 'subscription': SUBSCRIPTION_3['subscription'] })) @@ -366,7 +366,7 @@ async def test_unregistering_device_view_handles_save_error( } client = await mock_client(hass, hass_client, registrations) - with patch('homeassistant.components.notify.html5.save_json', + with patch('homeassistant.components.html5.notify.save_json', side_effect=HomeAssistantError()): resp = await client.delete(REGISTER_URL, data=json.dumps({ 'subscription': SUBSCRIPTION_1['subscription'], diff --git a/tests/components/pushbullet/__init__.py b/tests/components/pushbullet/__init__.py new file mode 100644 index 00000000000000..c7f7911950c25a --- /dev/null +++ b/tests/components/pushbullet/__init__.py @@ -0,0 +1 @@ +"""Tests for the pushbullet component.""" diff --git a/tests/components/notify/test_pushbullet.py b/tests/components/pushbullet/test_notify.py similarity index 100% rename from tests/components/notify/test_pushbullet.py rename to tests/components/pushbullet/test_notify.py diff --git a/tests/components/smtp/__init__.py b/tests/components/smtp/__init__.py new file mode 100644 index 00000000000000..a99f7991ff9611 --- /dev/null +++ b/tests/components/smtp/__init__.py @@ -0,0 +1 @@ +"""Tests for the smtp component.""" diff --git a/tests/components/notify/test_smtp.py b/tests/components/smtp/test_notify.py similarity index 96% rename from tests/components/notify/test_smtp.py rename to tests/components/smtp/test_notify.py index fa6c50032887ee..e946efe61e60a2 100644 --- a/tests/components/notify/test_smtp.py +++ b/tests/components/smtp/test_notify.py @@ -2,13 +2,13 @@ import unittest from unittest.mock import patch -from homeassistant.components.notify import smtp +from homeassistant.components.smtp.notify import MailNotificationService from tests.common import get_test_home_assistant import re -class MockSMTP(smtp.MailNotificationService): +class MockSMTP(MailNotificationService): """Test SMTP object that doesn't need a working server.""" def _send_email(self, msg): diff --git a/tests/components/yessssms/__init__.py b/tests/components/yessssms/__init__.py new file mode 100644 index 00000000000000..bf8e562009b91c --- /dev/null +++ b/tests/components/yessssms/__init__.py @@ -0,0 +1 @@ +"""Tests for the yessssms component.""" diff --git a/tests/components/notify/test_yessssms.py b/tests/components/yessssms/test_notify.py similarity index 90% rename from tests/components/notify/test_yessssms.py rename to tests/components/yessssms/test_notify.py index 42dd7be1aca9bf..837fee43f05108 100644 --- a/tests/components/notify/test_yessssms.py +++ b/tests/components/yessssms/test_notify.py @@ -1,7 +1,7 @@ """The tests for the notify yessssms platform.""" import unittest import requests_mock -from homeassistant.components.notify import yessssms +import homeassistant.components.yessssms.notify as yessssms class TestNotifyYesssSMS(unittest.TestCase): @@ -28,7 +28,7 @@ def test_login_error(self, mock): message = "Testing YesssSMS platform :)" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR'): self.yessssms.send_message(message) self.assertTrue(mock.called) @@ -37,7 +37,7 @@ def test_login_error(self, mock): def test_empty_message_error(self): """Test for an empty SMS message error.""" message = "" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR'): self.yessssms.send_message(message) @@ -54,7 +54,7 @@ def test_error_account_suspended(self, mock): message = "Testing YesssSMS platform :)" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR'): self.yessssms.send_message(message) self.assertTrue(mock.called) @@ -71,7 +71,7 @@ def test_error_account_suspended(self, mock): message = "Testing YesssSMS platform :)" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR'): self.yessssms.send_message(message) self.assertTrue(mock.called) @@ -83,7 +83,7 @@ def test_error_account_suspended_2(self): # pylint: disable=protected-access self.yessssms.yesss._suspended = True - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR') as context: self.yessssms.send_message(message) self.assertIn("Account is suspended, cannot send SMS.", @@ -124,7 +124,7 @@ def test_send_message(self, mock): status_code=200, ) - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='INFO') as context: self.yessssms.send_message(message) self.assertIn("SMS sent", context.output[0]) @@ -143,7 +143,7 @@ def test_no_recipient_error(self): # pylint: disable=protected-access self.yessssms._recipient = "" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR') as context: self.yessssms.send_message(message) @@ -179,7 +179,7 @@ def test_sms_sending_error(self, mock): message = "Testing YesssSMS platform :)" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR') as context: self.yessssms.send_message(message) @@ -199,7 +199,7 @@ def test_connection_error(self, mock): message = "Testing YesssSMS platform :)" - with self.assertLogs("homeassistant.components.notify", + with self.assertLogs("homeassistant.components.yessssms.notify", level='ERROR') as context: self.yessssms.send_message(message) From a69080ba734c720f920cdf97b78c86fff7a0b04b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 20:52:28 -0700 Subject: [PATCH 217/605] Print error instead of warning for custom platforms in legacy format (#22486) * Legacy platform format prints error * Enforce no partial overlays --- homeassistant/loader.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index e36ad5451c18ad..7f0d50f93d4973 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -99,10 +99,14 @@ def get_platform(hass, # type: HomeAssistant if platform is not None: return platform - # Legacy platform check: light/hue.py - platform = _load_file( - hass, PLATFORM_FORMAT.format(domain=platform_name, platform=domain), - base_paths) + # Legacy platform check for custom: custom_components/light/hue.py + # Only check if the component was also in custom components. + if component is None or base_paths[0] == PACKAGE_CUSTOM_COMPONENTS: + platform = _load_file( + hass, + PLATFORM_FORMAT.format(domain=platform_name, platform=domain), + [PACKAGE_CUSTOM_COMPONENTS] + ) if platform is None: if component is None: @@ -113,11 +117,10 @@ def get_platform(hass, # type: HomeAssistant _LOGGER.error("Unable to find platform %s.%s", platform_name, extra) return None - if platform.__name__.startswith(PACKAGE_CUSTOM_COMPONENTS): - _LOGGER.warning( - "Integrations need to be in their own folder. Change %s/%s.py to " - "%s/%s.py. This will stop working soon.", - domain, platform_name, platform_name, domain) + _LOGGER.error( + "Integrations need to be in their own folder. Change %s/%s.py to " + "%s/%s.py. This will stop working soon.", + domain, platform_name, platform_name, domain) return platform From ece9c62ee8ba1de6ad396f8d3e9f3a2945a27782 Mon Sep 17 00:00:00 2001 From: "Clifford W. Hansen" Date: Thu, 28 Mar 2019 06:20:43 +0200 Subject: [PATCH 218/605] Add game and app media types (#22459) * Added game and app media types * Changed media type to game from music * Removed app type as it is not used yet --- homeassistant/components/media_player/const.py | 1 + homeassistant/components/ps4/media_player.py | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_player/const.py b/homeassistant/components/media_player/const.py index 54e28d2d17e3d4..1e53e60736011d 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -38,6 +38,7 @@ MEDIA_TYPE_PLAYLIST = 'playlist' MEDIA_TYPE_IMAGE = 'image' MEDIA_TYPE_URL = 'url' +MEDIA_TYPE_GAME = 'game' SERVICE_CLEAR_PLAYLIST = 'clear_playlist' SERVICE_PLAY_MEDIA = 'play_media' diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index e2f0004f80e9dd..80c1fda52de055 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -12,7 +12,7 @@ from homeassistant.components.media_player import ( ENTITY_IMAGE_URL, MediaPlayerDevice) from homeassistant.components.media_player.const import ( - MEDIA_TYPE_MUSIC, SUPPORT_SELECT_SOURCE, SUPPORT_STOP, SUPPORT_TURN_OFF, + MEDIA_TYPE_GAME, SUPPORT_SELECT_SOURCE, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON) from homeassistant.const import ( ATTR_COMMAND, ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_REGION, @@ -328,8 +328,7 @@ def media_content_id(self): @property def media_content_type(self): """Content type of current playing media.""" - # No MEDIA_TYPE_GAME attr as of 0.90. - return MEDIA_TYPE_MUSIC + return MEDIA_TYPE_GAME @property def media_image_url(self): From 05cdab03b1ac3b88d1be193e10e7154c33c1d9c9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 21:32:40 -0700 Subject: [PATCH 219/605] Updated frontend to 20190327.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 5a10b60f12f3ad..80b5b744488f67 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190321.0'] +REQUIREMENTS = ['home-assistant-frontend==20190327.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 4f666d6e215448..4af334dd01ebda 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -547,7 +547,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190321.0 +home-assistant-frontend==20190327.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3cca2d4bf5e69c..60d9697ed191f7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -129,7 +129,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190321.0 +home-assistant-frontend==20190327.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 9d21afa444b600e4a8f72d1d6f7edc0c958f0444 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 21:32:58 -0700 Subject: [PATCH 220/605] Update translations --- .../.translations/zh-Hans.json | 19 +++++++++ .../components/auth/.translations/hu.json | 6 ++- .../components/axis/.translations/ca.json | 26 ++++++++++++ .../components/axis/.translations/da.json | 24 +++++++++++ .../components/axis/.translations/de.json | 26 ++++++++++++ .../components/axis/.translations/hu.json | 11 +++++ .../components/axis/.translations/ko.json | 26 ++++++++++++ .../components/axis/.translations/lb.json | 21 ++++++++++ .../components/axis/.translations/pl.json | 26 ++++++++++++ .../components/axis/.translations/ru.json | 26 ++++++++++++ .../components/axis/.translations/sl.json | 26 ++++++++++++ .../axis/.translations/zh-Hant.json | 26 ++++++++++++ .../components/daikin/.translations/hu.json | 2 +- .../components/daikin/.translations/ru.json | 2 +- .../components/deconz/.translations/hu.json | 2 +- .../components/deconz/.translations/ru.json | 2 +- .../components/deconz/.translations/sl.json | 2 +- .../ebusd/.translations/zh-Hans.json | 6 +++ .../emulated_roku/.translations/hu.json | 2 +- .../emulated_roku/.translations/zh-Hans.json | 6 ++- .../components/esphome/.translations/hu.json | 16 +++++--- .../components/esphome/.translations/ru.json | 2 +- .../components/esphome/.translations/sl.json | 6 ++- .../esphome/.translations/zh-Hans.json | 7 ++++ .../esphome/.translations/zh-Hant.json | 6 +-- .../gpslogger/.translations/zh-Hans.json | 10 ++++- .../components/hangouts/.translations/ru.json | 2 +- .../homekit_controller/.translations/da.json | 11 +++++ .../homekit_controller/.translations/sl.json | 33 +++++++++++++++ .../homematicip_cloud/.translations/ru.json | 2 +- .../components/hue/.translations/hu.json | 2 +- .../components/hue/.translations/ru.json | 2 +- .../ipma/.translations/zh-Hans.json | 19 +++++++++ .../locative/.translations/zh-Hans.json | 3 ++ .../mobile_app/.translations/hu.json | 14 +++++++ .../mobile_app/.translations/sl.json | 14 +++++++ .../moon/.translations/sensor.hu.json | 8 ++-- .../moon/.translations/sensor.lb.json | 8 +--- .../components/point/.translations/ru.json | 2 +- .../point/.translations/zh-Hans.json | 8 +++- .../components/ps4/.translations/ca.json | 9 ++++ .../components/ps4/.translations/de.json | 9 ++++ .../components/ps4/.translations/ko.json | 15 +++++-- .../components/ps4/.translations/lb.json | 9 ++++ .../components/ps4/.translations/ru.json | 15 +++++-- .../components/ps4/.translations/sl.json | 41 +++++++++++++++++++ .../components/ps4/.translations/zh-Hans.json | 1 + .../rainmachine/.translations/hu.json | 2 +- .../rainmachine/.translations/zh-Hans.json | 3 +- .../season/.translations/sensor.is.json | 8 ++++ .../smartthings/.translations/sl.json | 3 +- .../smartthings/.translations/zh-Hans.json | 28 +++++++++++++ .../tellduslive/.translations/en.json | 1 + .../tellduslive/.translations/hu.json | 2 +- .../tellduslive/.translations/ru.json | 4 +- .../tellduslive/.translations/zh-Hans.json | 10 +++-- .../components/toon/.translations/sl.json | 34 +++++++++++++++ .../components/tplink/.translations/sl.json | 15 +++++++ .../tplink/.translations/zh-Hans.json | 15 +++++++ .../components/tradfri/.translations/ru.json | 2 +- .../components/unifi/.translations/hu.json | 2 +- .../components/unifi/.translations/ru.json | 2 +- .../unifi/.translations/zh-Hans.json | 1 + .../components/upnp/.translations/en.json | 15 +++++++ .../components/upnp/.translations/hu.json | 2 +- .../components/upnp/.translations/ru.json | 2 +- .../components/zha/.translations/en.json | 1 - .../components/zha/.translations/zh-Hans.json | 1 + .../components/zwave/.translations/ru.json | 2 +- 69 files changed, 656 insertions(+), 60 deletions(-) create mode 100644 homeassistant/components/ambient_station/.translations/zh-Hans.json create mode 100644 homeassistant/components/axis/.translations/ca.json create mode 100644 homeassistant/components/axis/.translations/da.json create mode 100644 homeassistant/components/axis/.translations/de.json create mode 100644 homeassistant/components/axis/.translations/hu.json create mode 100644 homeassistant/components/axis/.translations/ko.json create mode 100644 homeassistant/components/axis/.translations/lb.json create mode 100644 homeassistant/components/axis/.translations/pl.json create mode 100644 homeassistant/components/axis/.translations/ru.json create mode 100644 homeassistant/components/axis/.translations/sl.json create mode 100644 homeassistant/components/axis/.translations/zh-Hant.json create mode 100644 homeassistant/components/ebusd/.translations/zh-Hans.json create mode 100644 homeassistant/components/homekit_controller/.translations/da.json create mode 100644 homeassistant/components/homekit_controller/.translations/sl.json create mode 100644 homeassistant/components/ipma/.translations/zh-Hans.json create mode 100644 homeassistant/components/mobile_app/.translations/hu.json create mode 100644 homeassistant/components/mobile_app/.translations/sl.json create mode 100644 homeassistant/components/ps4/.translations/sl.json create mode 100644 homeassistant/components/season/.translations/sensor.is.json create mode 100644 homeassistant/components/smartthings/.translations/zh-Hans.json create mode 100644 homeassistant/components/toon/.translations/sl.json create mode 100644 homeassistant/components/tplink/.translations/sl.json create mode 100644 homeassistant/components/tplink/.translations/zh-Hans.json diff --git a/homeassistant/components/ambient_station/.translations/zh-Hans.json b/homeassistant/components/ambient_station/.translations/zh-Hans.json new file mode 100644 index 00000000000000..866c06316f1cac --- /dev/null +++ b/homeassistant/components/ambient_station/.translations/zh-Hans.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "identifier_exists": "Application Key \u548c/\u6216 API Key \u5df2\u6ce8\u518c", + "invalid_key": "\u65e0\u6548\u7684 API \u5bc6\u94a5\u548c/\u6216 Application Key", + "no_devices": "\u6ca1\u6709\u5728\u5e10\u6237\u4e2d\u627e\u5230\u8bbe\u5907" + }, + "step": { + "user": { + "data": { + "api_key": "API Key", + "app_key": "Application Key" + }, + "title": "\u586b\u5199\u60a8\u7684\u4fe1\u606f" + } + }, + "title": "Ambient PWS" + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/hu.json b/homeassistant/components/auth/.translations/hu.json index a2d132d9073312..5e7b183509304b 100644 --- a/homeassistant/components/auth/.translations/hu.json +++ b/homeassistant/components/auth/.translations/hu.json @@ -9,13 +9,15 @@ }, "step": { "init": { - "description": "V\u00e1lassz \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1st:", + "description": "K\u00e9rlek, v\u00e1lassz egyet az \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1sok k\u00f6z\u00fcl:", "title": "\u00c1ll\u00edtsa be az \u00e9rtes\u00edt\u00e9si \u00f6sszetev\u0151 \u00e1ltal megadott egyszeri jelsz\u00f3t" }, "setup": { + "description": "Az egyszeri jelsz\u00f3 el lett k\u00fcldve a(z) **notify.{notify_service}** szolg\u00e1ltat\u00e1ssal. K\u00e9rlek, add meg al\u00e1bb:", "title": "Be\u00e1ll\u00edt\u00e1s ellen\u0151rz\u00e9se" } - } + }, + "title": "Egyszeri Jelsz\u00f3 \u00c9rtes\u00edt\u00e9s" }, "totp": { "error": { diff --git a/homeassistant/components/axis/.translations/ca.json b/homeassistant/components/axis/.translations/ca.json new file mode 100644 index 00000000000000..5e98dbf34189d1 --- /dev/null +++ b/homeassistant/components/axis/.translations/ca.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "bad_config_file": "Dades incorrectes del fitxer de configuraci\u00f3", + "link_local_address": "L'enlla\u00e7 d'adreces locals no est\u00e0 disponible" + }, + "error": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "device_unavailable": "El dispositiu no est\u00e0 disponible", + "faulty_credentials": "Credencials d'usuari incorrectes" + }, + "step": { + "user": { + "data": { + "host": "Amfitri\u00f3", + "password": "Contrasenya", + "port": "Port", + "username": "Nom d'usuari" + }, + "title": "Configuraci\u00f3 de dispositiu Axis" + } + }, + "title": "Dispositiu Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/da.json b/homeassistant/components/axis/.translations/da.json new file mode 100644 index 00000000000000..4657d2fb355324 --- /dev/null +++ b/homeassistant/components/axis/.translations/da.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "Enheden er allerede konfigureret" + }, + "error": { + "already_configured": "Enheden er allerede konfigureret", + "device_unavailable": "Enheden er ikke tilg\u00e6ngelig", + "faulty_credentials": "Ugyldige legitimationsoplysninger" + }, + "step": { + "user": { + "data": { + "host": "V\u00e6rt", + "password": "Adgangskode", + "port": "Port", + "username": "Brugernavn" + }, + "title": "Konfigurer Axis enhed" + } + }, + "title": "Axis enhed" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/de.json b/homeassistant/components/axis/.translations/de.json new file mode 100644 index 00000000000000..c979068b922229 --- /dev/null +++ b/homeassistant/components/axis/.translations/de.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "bad_config_file": "Fehlerhafte Daten aus der Konfigurationsdatei", + "link_local_address": "Link-local Adressen werden nicht unterst\u00fctzt" + }, + "error": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "device_unavailable": "Ger\u00e4t ist nicht verf\u00fcgbar", + "faulty_credentials": "Ung\u00fcltige Anmeldeinformationen" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Passwort", + "port": "Port", + "username": "Benutzername" + }, + "title": "Axis Ger\u00e4t einrichten" + } + }, + "title": "Axis Ger\u00e4t" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/hu.json b/homeassistant/components/axis/.translations/hu.json new file mode 100644 index 00000000000000..cbf055e2fba477 --- /dev/null +++ b/homeassistant/components/axis/.translations/hu.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Hoszt" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/ko.json b/homeassistant/components/axis/.translations/ko.json new file mode 100644 index 00000000000000..f1543afbae8a39 --- /dev/null +++ b/homeassistant/components/axis/.translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "bad_config_file": "\uad6c\uc131 \ud30c\uc77c\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "link_local_address": "\ub85c\uceec \uc8fc\uc18c \uc5f0\uacb0\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4" + }, + "error": { + "already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "device_unavailable": "\uc7a5\uce58\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "faulty_credentials": "\uc0ac\uc6a9\uc790 \uc774\ub984 \ud639\uc740 \ube44\ubc00\ubc88\ud638\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "title": "Axis \uc7a5\uce58 \uc124\uc815" + } + }, + "title": "Axis \uc7a5\uce58" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/lb.json b/homeassistant/components/axis/.translations/lb.json new file mode 100644 index 00000000000000..e0f6ebc1553535 --- /dev/null +++ b/homeassistant/components/axis/.translations/lb.json @@ -0,0 +1,21 @@ +{ + "config": { + "error": { + "already_configured": "Apparat ass scho konfigur\u00e9iert", + "device_unavailable": "Apparat ass net erreechbar", + "faulty_credentials": "Ong\u00eblteg Login Informatioune" + }, + "step": { + "user": { + "data": { + "host": "Apparat", + "password": "Passwuert", + "port": "Port", + "username": "Benotzernumm" + }, + "title": "Axis Apparat ariichten" + } + }, + "title": "Axis Apparat" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/pl.json b/homeassistant/components/axis/.translations/pl.json new file mode 100644 index 00000000000000..7903dc63bf8bca --- /dev/null +++ b/homeassistant/components/axis/.translations/pl.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "bad_config_file": "B\u0142\u0119dne dane z pliku konfiguracyjnego", + "link_local_address": "Po\u0142\u0105czenie lokalnego adresu nie jest obs\u0142ugiwane" + }, + "error": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "device_unavailable": "Urz\u0105dzenie jest niedost\u0119pne", + "faulty_credentials": "B\u0142\u0119dne dane uwierzytelniaj\u0105ce" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Has\u0142o", + "port": "Port", + "username": "Nazwa u\u017cytkownika" + }, + "title": "Konfiguracja urz\u0105dzenia Axis" + } + }, + "title": "Urz\u0105dzenie Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/ru.json b/homeassistant/components/axis/.translations/ru.json new file mode 100644 index 00000000000000..f303aa947ea8ba --- /dev/null +++ b/homeassistant/components/axis/.translations/ru.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", + "bad_config_file": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438", + "link_local_address": "\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f" + }, + "error": { + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", + "device_unavailable": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e", + "faulty_credentials": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435" + }, + "step": { + "user": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "port": "\u041f\u043e\u0440\u0442", + "username": "\u041b\u043e\u0433\u0438\u043d" + }, + "title": "Axis" + } + }, + "title": "Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/sl.json b/homeassistant/components/axis/.translations/sl.json new file mode 100644 index 00000000000000..41d2994987333f --- /dev/null +++ b/homeassistant/components/axis/.translations/sl.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Naprava je \u017ee konfigurirana", + "bad_config_file": "Napa\u010dni podatki iz konfiguracijske datoteke", + "link_local_address": "Lokalni naslovi povezave niso podprti" + }, + "error": { + "already_configured": "Naprava je \u017ee konfigurirana", + "device_unavailable": "Naprava ni na voljo", + "faulty_credentials": "Napa\u010dni uporabni\u0161ki podatki" + }, + "step": { + "user": { + "data": { + "host": "Gostitelj", + "password": "Geslo", + "port": "Vrata", + "username": "Uporabni\u0161ko ime" + }, + "title": "Nastavite plo\u0161\u010dek" + } + }, + "title": "Plo\u0161\u010dek" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/zh-Hant.json b/homeassistant/components/axis/.translations/zh-Hant.json new file mode 100644 index 00000000000000..ac9f3ceb2b696f --- /dev/null +++ b/homeassistant/components/axis/.translations/zh-Hant.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "bad_config_file": "\u8a2d\u5b9a\u6a94\u6848\u8cc7\u6599\u7121\u6548", + "link_local_address": "\u4e0d\u652f\u63f4\u9023\u7d50\u672c\u5730\u7aef\u4f4d\u5740" + }, + "error": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "device_unavailable": "\u88dd\u7f6e\u7121\u6cd5\u4f7f\u7528", + "faulty_credentials": "\u4f7f\u7528\u8005\u6191\u8b49\u7121\u6548" + }, + "step": { + "user": { + "data": { + "host": "\u4e3b\u6a5f\u7aef", + "password": "\u5bc6\u78bc", + "port": "\u901a\u8a0a\u57e0", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + }, + "title": "\u8a2d\u5b9a Axis \u88dd\u7f6e" + } + }, + "title": "Axis \u88dd\u7f6e" + } +} \ No newline at end of file diff --git a/homeassistant/components/daikin/.translations/hu.json b/homeassistant/components/daikin/.translations/hu.json index cbca935f5517c4..f433a6215b85ad 100644 --- a/homeassistant/components/daikin/.translations/hu.json +++ b/homeassistant/components/daikin/.translations/hu.json @@ -8,7 +8,7 @@ "step": { "user": { "data": { - "host": "Kiszolg\u00e1l\u00f3" + "host": "Hoszt" }, "description": "Add meg a Daikin l\u00e9gkond\u00edcion\u00e1l\u00f3 IP-c\u00edm\u00e9t.", "title": "A Daikin l\u00e9gkond\u00edcion\u00e1l\u00f3 konfigur\u00e1l\u00e1sa" diff --git a/homeassistant/components/daikin/.translations/ru.json b/homeassistant/components/daikin/.translations/ru.json index 0549aa3b160b7b..ce1f1ab3caa974 100644 --- a/homeassistant/components/daikin/.translations/ru.json +++ b/homeassistant/components/daikin/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "device_fail": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", "device_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443." }, diff --git a/homeassistant/components/deconz/.translations/hu.json b/homeassistant/components/deconz/.translations/hu.json index 06211f61bf2183..5bf8db4684190f 100644 --- a/homeassistant/components/deconz/.translations/hu.json +++ b/homeassistant/components/deconz/.translations/hu.json @@ -11,7 +11,7 @@ "step": { "init": { "data": { - "host": "H\u00e1zigazda (Host)", + "host": "Hoszt", "port": "Port" }, "title": "deCONZ \u00e1tj\u00e1r\u00f3 megad\u00e1sa" diff --git a/homeassistant/components/deconz/.translations/ru.json b/homeassistant/components/deconz/.translations/ru.json index c92f1562157a6b..5fd31ab9d8f2c0 100644 --- a/homeassistant/components/deconz/.translations/ru.json +++ b/homeassistant/components/deconz/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "no_bridges": "\u0428\u043b\u044e\u0437\u044b deCONZ \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b", "one_instance_only": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 deCONZ" }, diff --git a/homeassistant/components/deconz/.translations/sl.json b/homeassistant/components/deconz/.translations/sl.json index cea6f8ef4dd65e..686bb5b1e2eb61 100644 --- a/homeassistant/components/deconz/.translations/sl.json +++ b/homeassistant/components/deconz/.translations/sl.json @@ -17,7 +17,7 @@ "title": "Dolo\u010dite deCONZ prehod" }, "link": { - "description": "Odklenite va\u0161 deCONZ gateway za registracijo z Home Assistant-om. \n1. Pojdite v deCONT sistemske nastavitve\n2. Pritisnite tipko \"odkleni prehod\"", + "description": "Odklenite va\u0161 deCONZ gateway za registracijo s Home Assistant-om. \n1. Pojdite v deCONZ sistemske nastavitve\n2. Pritisnite tipko \"odkleni prehod\"", "title": "Povezava z deCONZ" }, "options": { diff --git a/homeassistant/components/ebusd/.translations/zh-Hans.json b/homeassistant/components/ebusd/.translations/zh-Hans.json new file mode 100644 index 00000000000000..c43ca27b22a09c --- /dev/null +++ b/homeassistant/components/ebusd/.translations/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "state": { + "day": "\u65e5", + "night": "\u591c" + } +} \ No newline at end of file diff --git a/homeassistant/components/emulated_roku/.translations/hu.json b/homeassistant/components/emulated_roku/.translations/hu.json index c38e6890d8a3c4..9b6f77062537e3 100644 --- a/homeassistant/components/emulated_roku/.translations/hu.json +++ b/homeassistant/components/emulated_roku/.translations/hu.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "host_ip": "H\u00e1zigazda IP", + "host_ip": "Hoszt IP", "listen_port": "Port figyel\u00e9se", "name": "N\u00e9v" }, diff --git a/homeassistant/components/emulated_roku/.translations/zh-Hans.json b/homeassistant/components/emulated_roku/.translations/zh-Hans.json index 5ff0466c9bcbfa..88d8a822696f54 100644 --- a/homeassistant/components/emulated_roku/.translations/zh-Hans.json +++ b/homeassistant/components/emulated_roku/.translations/zh-Hans.json @@ -10,10 +10,12 @@ "advertise_port": "\u5e7f\u64ad\u7aef\u53e3", "host_ip": "\u4e3b\u673a IP", "listen_port": "\u76d1\u542c\u7aef\u53e3", - "name": "\u59d3\u540d" + "name": "\u59d3\u540d", + "upnp_bind_multicast": "\u7ed1\u5b9a\u591a\u64ad (True/False)" }, "title": "\u5b9a\u4e49\u670d\u52a1\u5668\u914d\u7f6e" } - } + }, + "title": "EmulatedRoku" } } \ No newline at end of file diff --git a/homeassistant/components/esphome/.translations/hu.json b/homeassistant/components/esphome/.translations/hu.json index 1e72bd8030cd5f..c665637ba05248 100644 --- a/homeassistant/components/esphome/.translations/hu.json +++ b/homeassistant/components/esphome/.translations/hu.json @@ -4,22 +4,28 @@ "already_configured": "Az ESP-t m\u00e1r konfigur\u00e1ltad." }, "error": { - "connection_error": "Nem tud csatlakozni az ESP-hez. K\u00e9rlek gy\u0151z\u0151dj meg r\u00f3la, hogy a YAML f\u00e1jl tartalmaz egy \"api:\" sort.", - "invalid_password": "\u00c9rv\u00e9nytelen jelsz\u00f3!" + "connection_error": "Nem lehet csatlakozni az ESP-hez. K\u00e9rlek gy\u0151z\u0151dj meg r\u00f3la, hogy a YAML f\u00e1jl tartalmaz egy \"api:\" sort.", + "invalid_password": "\u00c9rv\u00e9nytelen jelsz\u00f3!", + "resolve_error": "Az ESP c\u00edme nem oldhat\u00f3 fel. Ha a hiba tov\u00e1bbra is fenn\u00e1ll, k\u00e9rlek, \u00e1ll\u00edts be egy statikus IP-c\u00edmet: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips" }, "step": { "authenticate": { "data": { "password": "Jelsz\u00f3" }, - "description": "K\u00e9rj\u00fck, add meg a konfigur\u00e1ci\u00f3ban be\u00e1ll\u00edtott jelsz\u00f3t.", - "title": "Adja meg a jelsz\u00f3t" + "description": "K\u00e9rlek, add meg a konfigur\u00e1ci\u00f3ban {name} n\u00e9vhez be\u00e1ll\u00edtott jelsz\u00f3t.", + "title": "Add meg a jelsz\u00f3t" + }, + "discovery_confirm": { + "description": "Szeretn\u00e9d hozz\u00e1adni a(z) `{name}` ESPHome csom\u00f3pontot a Home Assistant-hoz?", + "title": "Felfedezett ESPHome csom\u00f3pont" }, "user": { "data": { - "host": "Kiszolg\u00e1l\u00f3", + "host": "Hoszt", "port": "Port" }, + "description": "K\u00e9rlek, add meg az [ESPHome](https://esphomelib.com/) csom\u00f3pontod kapcsol\u00f3d\u00e1si be\u00e1ll\u00edt\u00e1sait.", "title": "ESPHome" } }, diff --git a/homeassistant/components/esphome/.translations/ru.json b/homeassistant/components/esphome/.translations/ru.json index 2b631ea219c4b9..9777a920a944e3 100644 --- a/homeassistant/components/esphome/.translations/ru.json +++ b/homeassistant/components/esphome/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430" }, "error": { "connection_error": "\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a ESP. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 YAML-\u0444\u0430\u0439\u043b \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 'api:'.", diff --git a/homeassistant/components/esphome/.translations/sl.json b/homeassistant/components/esphome/.translations/sl.json index 7745c45501587b..93ca607aabec0b 100644 --- a/homeassistant/components/esphome/.translations/sl.json +++ b/homeassistant/components/esphome/.translations/sl.json @@ -13,9 +13,13 @@ "data": { "password": "Geslo" }, - "description": "Vnesite geslo, ki ste ga nastavili v svoji konfiguraciji.", + "description": "Vnesite geslo, ki ste ga nastavili v konfiguraciji za {name}.", "title": "Vnesite geslo" }, + "discovery_confirm": { + "description": "\u017delite dodati ESPHome vozli\u0161\u010de ` {name} ` v Home Assistant?", + "title": "Odkrita ESPHome vozli\u0161\u010da" + }, "user": { "data": { "host": "Gostitelj", diff --git a/homeassistant/components/esphome/.translations/zh-Hans.json b/homeassistant/components/esphome/.translations/zh-Hans.json index 0a8211be449767..46790868aba611 100644 --- a/homeassistant/components/esphome/.translations/zh-Hans.json +++ b/homeassistant/components/esphome/.translations/zh-Hans.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "ESP \u5df2\u914d\u7f6e\u5b8c\u6210" + }, "error": { "connection_error": "\u65e0\u6cd5\u8fde\u63a5\u5230 ESP\u3002\u8bf7\u786e\u8ba4\u60a8\u7684 YAML \u6587\u4ef6\u4e2d\u5305\u542b 'api:' \u884c\u3002", "invalid_password": "\u65e0\u6548\u7684\u5bc6\u7801\uff01", @@ -13,6 +16,10 @@ "description": "\u8bf7\u8f93\u5165\u60a8\u5728\u914d\u7f6e\u4e2d\u4e3a\u201c{name}\u201d\u8bbe\u7f6e\u7684\u5bc6\u7801\u3002", "title": "\u8f93\u5165\u5bc6\u7801" }, + "discovery_confirm": { + "description": "\u662f\u5426\u8981\u5c06 ESPHome \u8282\u70b9 `{name}` \u6dfb\u52a0\u5230 Home Assistant\uff1f", + "title": "\u53d1\u73b0\u4e86 ESPHome \u8282\u70b9" + }, "user": { "data": { "host": "\u4e3b\u673a", diff --git a/homeassistant/components/esphome/.translations/zh-Hant.json b/homeassistant/components/esphome/.translations/zh-Hant.json index 65817470860a15..9a5821f0b8fe41 100644 --- a/homeassistant/components/esphome/.translations/zh-Hant.json +++ b/homeassistant/components/esphome/.translations/zh-Hant.json @@ -17,15 +17,15 @@ "title": "\u8f38\u5165\u5bc6\u78bc" }, "discovery_confirm": { - "description": "\u662f\u5426\u8981\u5c07 ESPHome node\u300c{name}\u300d\u65b0\u589e\u81f3 Home Assistant\uff1f", - "title": "\u81ea\u52d5\u63a2\u7d22\u5230\u7684 ESPHome node" + "description": "\u662f\u5426\u8981\u5c07 ESPHome \u7bc0\u9ede\u300c{name}\u300d\u65b0\u589e\u81f3 Home Assistant\uff1f", + "title": "\u767c\u73fe\u5230 ESPHome \u7bc0\u9ede" }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", "port": "\u901a\u8a0a\u57e0" }, - "description": "\u8acb\u8f38\u5165 [ESPHome](https://esphomelib.com/) node \u9023\u7dda\u8cc7\u8a0a\u3002", + "description": "\u8acb\u8f38\u5165 [ESPHome](https://esphomelib.com/) \u7bc0\u9ede\u9023\u7dda\u8cc7\u8a0a\u3002", "title": "ESPHome" } }, diff --git a/homeassistant/components/gpslogger/.translations/zh-Hans.json b/homeassistant/components/gpslogger/.translations/zh-Hans.json index 91d3ac74994a68..f99efa91c61965 100644 --- a/homeassistant/components/gpslogger/.translations/zh-Hans.json +++ b/homeassistant/components/gpslogger/.translations/zh-Hans.json @@ -1,10 +1,18 @@ { "config": { "abort": { + "not_internet_accessible": "\u60a8\u7684 Home Assistant \u5b9e\u4f8b\u9700\u8981\u53ef\u4ece\u4e92\u8054\u7f51\u8bbf\u95ee\u4ee5\u63a5\u6536 GPSLogger \u6d88\u606f\u3002", "one_instance_allowed": "\u53ea\u6709\u4e00\u4e2a\u5b9e\u4f8b\u662f\u5fc5\u9700\u7684\u3002" }, "create_entry": { "default": "\u8981\u5411 Home Assistant \u53d1\u9001\u4e8b\u4ef6\uff0c\u60a8\u9700\u8981\u914d\u7f6e GPSLogger \u7684 Webhook \u529f\u80fd\u3002\n\n\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\uff1a\n\n- URL: `{webhook_url}`\n- Method: POST\n\n\u8bf7\u53c2\u9605[\u6587\u6863]({docs_url})\u4ee5\u4e86\u89e3\u66f4\u591a\u4fe1\u606f\u3002" - } + }, + "step": { + "user": { + "description": "\u60a8\u786e\u5b9a\u8981\u8bbe\u7f6e GPSLogger Webhook \u5417\uff1f", + "title": "\u8bbe\u7f6e GPSLogger Webhook" + } + }, + "title": "GPSLogger Webhook" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/ru.json b/homeassistant/components/hangouts/.translations/ru.json index 6d93ec0d18f3b8..143d6bc88ba111 100644 --- a/homeassistant/components/hangouts/.translations/ru.json +++ b/homeassistant/components/hangouts/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430" }, "error": { diff --git a/homeassistant/components/homekit_controller/.translations/da.json b/homeassistant/components/homekit_controller/.translations/da.json new file mode 100644 index 00000000000000..3451053eb072a3 --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/da.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "device": "Enhed" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/sl.json b/homeassistant/components/homekit_controller/.translations/sl.json new file mode 100644 index 00000000000000..afee189216d365 --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/sl.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "Dodatna oprema je \u017ee konfigurirana s tem krmilnikom.", + "already_paired": "Ta dodatna oprema je \u017ee povezana z drugo napravo. Ponastavite dodatno opremo in poskusite znova.", + "ignored_model": "Podpora za HomeKit za ta model je blokirana, saj je na voljo ve\u010d funkcij popolne nativne integracije.", + "invalid_config_entry": "Ta naprava se prikazuje kot pripravljena za povezavo, vendar je konflikt v nastavitvah Home Assistant, ki ga je treba najprej odstraniti.", + "no_devices": "Ni bilo mogo\u010de najti neuparjenih naprav" + }, + "error": { + "authentication_error": "Nepravilna koda HomeKit. Preverite in poskusite znova.", + "unable_to_pair": "Ni mogo\u010de seznaniti. Poskusite znova.", + "unknown_error": "Naprava je sporo\u010dila neznano napako. Seznanjanje ni uspelo." + }, + "step": { + "pair": { + "data": { + "pairing_code": "Koda za seznanjanje" + }, + "description": "Vnesi HomeKit kodo, \u010de \u017eeli\u0161 uporabiti to dodatno opremo", + "title": "Seznanite s HomeKit Opremo" + }, + "user": { + "data": { + "device": "Naprava" + }, + "description": "Izberite napravo, s katero se \u017eelite seznaniti", + "title": "Seznanite s HomeKit Opremo" + } + }, + "title": "HomeKit oprema" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/ru.json b/homeassistant/components/homematicip_cloud/.translations/ru.json index e1aec6162f4c28..dde9345b2d2dc7 100644 --- a/homeassistant/components/homematicip_cloud/.translations/ru.json +++ b/homeassistant/components/homematicip_cloud/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0422\u043e\u0447\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "connection_aborted": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 HMIP", "unknown": "\u0412\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, diff --git a/homeassistant/components/hue/.translations/hu.json b/homeassistant/components/hue/.translations/hu.json index 4d534eace7de78..e65286b5c646f0 100644 --- a/homeassistant/components/hue/.translations/hu.json +++ b/homeassistant/components/hue/.translations/hu.json @@ -15,7 +15,7 @@ "step": { "init": { "data": { - "host": "H\u00e1zigazda (Host)" + "host": "Hoszt" }, "title": "V\u00e1lassz Hue bridge-t" }, diff --git a/homeassistant/components/hue/.translations/ru.json b/homeassistant/components/hue/.translations/ru.json index b6e2ccce8ed392..ce71fb670be424 100644 --- a/homeassistant/components/hue/.translations/ru.json +++ b/homeassistant/components/hue/.translations/ru.json @@ -2,7 +2,7 @@ "config": { "abort": { "all_configured": "\u0412\u0441\u0435 Philips Hue \u0448\u043b\u044e\u0437\u044b \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b", - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0448\u043b\u044e\u0437\u0443", "discover_timeout": "\u0428\u043b\u044e\u0437 Philips Hue \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d", "no_bridges": "\u0428\u043b\u044e\u0437\u044b Philips Hue \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b", diff --git a/homeassistant/components/ipma/.translations/zh-Hans.json b/homeassistant/components/ipma/.translations/zh-Hans.json new file mode 100644 index 00000000000000..6c5654b6388e9f --- /dev/null +++ b/homeassistant/components/ipma/.translations/zh-Hans.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "name_exists": "\u540d\u79f0\u5df2\u5b58\u5728" + }, + "step": { + "user": { + "data": { + "latitude": "\u7eac\u5ea6", + "longitude": "\u7ecf\u5ea6", + "name": "\u540d\u79f0" + }, + "description": "\u8461\u8404\u7259\u56fd\u5bb6\u5927\u6c14\u7814\u7a76\u6240", + "title": "\u4f4d\u7f6e" + } + }, + "title": "\u8461\u8404\u7259\u6c14\u8c61\u670d\u52a1\uff08IPMA\uff09" + } +} \ No newline at end of file diff --git a/homeassistant/components/locative/.translations/zh-Hans.json b/homeassistant/components/locative/.translations/zh-Hans.json index 967671de535885..d6c831d5a0e127 100644 --- a/homeassistant/components/locative/.translations/zh-Hans.json +++ b/homeassistant/components/locative/.translations/zh-Hans.json @@ -4,6 +4,9 @@ "not_internet_accessible": "\u60a8\u7684 Home Assistant \u5b9e\u4f8b\u9700\u8981\u53ef\u4ece\u4e92\u8054\u7f51\u8bbf\u95ee\u4ee5\u63a5\u6536 Geofency \u6d88\u606f\u3002", "one_instance_allowed": "\u53ea\u6709\u4e00\u4e2a\u5b9e\u4f8b\u662f\u5fc5\u9700\u7684\u3002" }, + "create_entry": { + "default": "\u8981\u5411 Home Assistant \u53d1\u9001\u4e8b\u4ef6\uff0c\u60a8\u9700\u8981\u914d\u7f6e Locative app \u7684 Webhook \u529f\u80fd\u3002\n\n\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\uff1a\n\n- URL: `{webhook_url}`\n- Method: POST\n\n\u8bf7\u53c2\u9605[\u6587\u6863]({docs_url})\u4ee5\u4e86\u89e3\u66f4\u591a\u4fe1\u606f\u3002" + }, "step": { "user": { "description": "\u60a8\u786e\u5b9a\u8981\u8bbe\u7f6e\u5b9a\u4f4d Webhook\u5417\uff1f", diff --git a/homeassistant/components/mobile_app/.translations/hu.json b/homeassistant/components/mobile_app/.translations/hu.json new file mode 100644 index 00000000000000..e95f4743ae3d0c --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/hu.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Nyisd meg a mobil alkalmaz\u00e1st a Home Assistant-tal val\u00f3 integr\u00e1ci\u00f3hoz. A kompatibilis alkalmaz\u00e1sok list\u00e1j\u00e1nak megtekint\u00e9s\u00e9hez ellen\u0151rizd [a le\u00edr\u00e1st]({apps_url})." + }, + "step": { + "confirm": { + "description": "Be szeretn\u00e9d \u00e1ll\u00edtani a mobil alkalmaz\u00e1s komponenst?", + "title": "Mobil alkalmaz\u00e1s" + } + }, + "title": "Mobil alkalmaz\u00e1s" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/sl.json b/homeassistant/components/mobile_app/.translations/sl.json new file mode 100644 index 00000000000000..6236421ffce317 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/sl.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Odprite mobilno aplikacijo, da nastavite integracijo s storitvijo Home Assistant. Za seznam zdru\u017eljivih aplikacij si oglejte [docs] ({apps_url})." + }, + "step": { + "confirm": { + "description": "Ali \u017eelite nastaviti komponento aplikacije Mobile App?", + "title": "Mobilna Aplikacija" + } + }, + "title": "Mobilna Aplikacija" + } +} \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.hu.json b/homeassistant/components/moon/.translations/sensor.hu.json index 0fcd02a6961471..fff9f51f50d5af 100644 --- a/homeassistant/components/moon/.translations/sensor.hu.json +++ b/homeassistant/components/moon/.translations/sensor.hu.json @@ -4,9 +4,9 @@ "full_moon": "Telihold", "last_quarter": "Utols\u00f3 negyed", "new_moon": "\u00dajhold", - "waning_crescent": "Fogy\u00f3 Hold (sarl\u00f3)", - "waning_gibbous": "Fogy\u00f3 Hold", - "waxing_crescent": "N\u00f6v\u0151 Hold (sarl\u00f3)", - "waxing_gibbous": "N\u00f6v\u0151 Hold" + "waning_crescent": "Fogy\u00f3 holdsarl\u00f3", + "waning_gibbous": "Fogy\u00f3 hold", + "waxing_crescent": "N\u00f6v\u0151 holdsarl\u00f3", + "waxing_gibbous": "N\u00f6v\u0151 hold" } } \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.lb.json b/homeassistant/components/moon/.translations/sensor.lb.json index 2aa7ea03db7c28..d2f95685634309 100644 --- a/homeassistant/components/moon/.translations/sensor.lb.json +++ b/homeassistant/components/moon/.translations/sensor.lb.json @@ -1,12 +1,6 @@ { "state": { - "first_quarter": "\u00c9ischt V\u00e9ierel", "full_moon": "Vollmound", - "last_quarter": "L\u00e4scht V\u00e9ierel", - "new_moon": "Neimound", - "waning_crescent": "Ofhuelende Mound", - "waning_gibbous": "Dr\u00ebtt V\u00e9ierel", - "waxing_crescent": "Zouhuelende Mound", - "waxing_gibbous": "Zweet V\u00e9ierel" + "new_moon": "Neimound" } } \ No newline at end of file diff --git a/homeassistant/components/point/.translations/ru.json b/homeassistant/components/point/.translations/ru.json index 60c1d62ab911bd..d2f3f90cb7792a 100644 --- a/homeassistant/components/point/.translations/ru.json +++ b/homeassistant/components/point/.translations/ru.json @@ -4,7 +4,7 @@ "already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", "authorize_url_fail": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", - "external_setup": "\u0422\u043e\u0447\u043a\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430.", + "external_setup": "Point \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430.", "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Point \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/point/)." }, "create_entry": { diff --git a/homeassistant/components/point/.translations/zh-Hans.json b/homeassistant/components/point/.translations/zh-Hans.json index 16d1bddbaf707b..e171aedf1ceee0 100644 --- a/homeassistant/components/point/.translations/zh-Hans.json +++ b/homeassistant/components/point/.translations/zh-Hans.json @@ -1,8 +1,14 @@ { "config": { "abort": { + "already_setup": "\u60a8\u53ea\u80fd\u914d\u7f6e\u4e00\u4e2a Point \u5e10\u6237\u3002", "authorize_url_fail": "\u751f\u6210\u6388\u6743\u7f51\u5740\u65f6\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002", - "authorize_url_timeout": "\u751f\u6210\u6388\u6743\u7f51\u5740\u8d85\u65f6\u3002" + "authorize_url_timeout": "\u751f\u6210\u6388\u6743\u7f51\u5740\u8d85\u65f6\u3002", + "external_setup": "Point\u914d\u7f6e\u6210\u529f\u3002", + "no_flows": "\u60a8\u9700\u8981\u5148\u914d\u7f6e Point\uff0c\u7136\u540e\u624d\u80fd\u5bf9\u5176\u8fdb\u884c\u6388\u6743\u3002 [\u8bf7\u9605\u8bfb\u8bf4\u660e](https://www.home-assistant.io/components/point/)\u3002" + }, + "create_entry": { + "default": "\u4f7f\u7528 Minut \u4e3a\u60a8\u7684 Point \u8bbe\u5907\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u6210\u529f" }, "error": { "follow_link": "\u8bf7\u5728\u70b9\u51fb\u63d0\u4ea4\u524d\u6309\u7167\u94fe\u63a5\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1", diff --git a/homeassistant/components/ps4/.translations/ca.json b/homeassistant/components/ps4/.translations/ca.json index 350b65ca815d4f..5e4b572ab452d4 100644 --- a/homeassistant/components/ps4/.translations/ca.json +++ b/homeassistant/components/ps4/.translations/ca.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "No s'ha pogut sincronitzar amb la PlayStation 4. Verifica el codi PIN.", + "no_ipaddress": "Introdueix l'adre\u00e7a IP de la PlayStation 4 que vulguis configurar.", "not_ready": "La PlayStation 4 no est\u00e0 engegada o no s'ha connectada a la xarxa." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Introdueix la informaci\u00f3 de la teva PlayStation 4. Pel 'PIN', ves a 'Configuraci\u00f3' de la PlayStation 4, despr\u00e9s navega fins a 'Configuraci\u00f3 de la connexi\u00f3 de l'aplicaci\u00f3 m\u00f2bil' i selecciona 'Afegir dispositiu'. Introdueix el PIN que es mostra.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "Adre\u00e7a IP (deixa-ho en blanc si fas servir la detecci\u00f3 autom\u00e0tica).", + "mode": "Mode de configuraci\u00f3" + }, + "description": "Selecciona el mode de configuraci\u00f3. El camp de l'adre\u00e7a IP es pot deixar en blanc si selecciones descobriment autom\u00e0tic (els dispositius es descobriran autom\u00e0ticament).", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/de.json b/homeassistant/components/ps4/.translations/de.json index 8f4e8838673175..e2eadb1fe30362 100644 --- a/homeassistant/components/ps4/.translations/de.json +++ b/homeassistant/components/ps4/.translations/de.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "Fehler beim Koppeln mit PlayStation 4. \u00dcberpr\u00fcfe, ob die PIN korrekt ist.", + "no_ipaddress": "Gib die IP-Adresse der PlayStation 4 ein, die konfiguriert werden soll.", "not_ready": "PlayStation 4 ist nicht eingeschaltet oder mit dem Netzwerk verbunden." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Geben Sie Ihre PlayStation 4-Informationen ein. Navigiere f\u00fcr \"PIN\" auf der PlayStation 4-Konsole zu \"Einstellungen\". Navigiere dann zu \"Mobile App-Verbindungseinstellungen\" und w\u00e4hle \"Ger\u00e4t hinzuf\u00fcgen\" aus. Gib die angezeigte PIN ein.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP-Adresse (Leer lassen, wenn automatische Erkennung verwendet wird).", + "mode": "Konfigurationsmodus" + }, + "description": "W\u00e4hlen Sie den Modus f\u00fcr die Konfiguration aus. Das Feld IP-Adresse kann leer bleiben, wenn die automatische Erkennung ausgew\u00e4hlt wird, da Ger\u00e4te automatisch erkannt werden.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/ko.json b/homeassistant/components/ps4/.translations/ko.json index ca77537e4e1117..ba864f07320012 100644 --- a/homeassistant/components/ps4/.translations/ko.json +++ b/homeassistant/components/ps4/.translations/ko.json @@ -4,11 +4,12 @@ "credential_error": "\uc790\uaca9 \uc99d\uba85\uc744 \uac00\uc838\uc624\ub294 \uc911 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", "devices_configured": "\ubc1c\uacac \ub41c \ubaa8\ub4e0 \uae30\uae30\ub294 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "no_devices_found": "PlayStation 4 \uae30\uae30\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", - "port_987_bind_error": "\ud3ec\ud2b8 987 \uc5d0 \ubc14\uc778\ub529 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", - "port_997_bind_error": "\ud3ec\ud2b8 997 \uc5d0 \ubc14\uc778\ub529 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + "port_987_bind_error": "\ud3ec\ud2b8 987 \uc5d0 \ubc14\uc778\ub529 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ucd94\uac00 \uc815\ubcf4\ub294 [\uc548\ub0b4](https://www.home-assistant.io/components/ps4/) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "port_997_bind_error": "\ud3ec\ud2b8 997 \uc5d0 \ubc14\uc778\ub529 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ucd94\uac00 \uc815\ubcf4\ub294 [\uc548\ub0b4](https://www.home-assistant.io/components/ps4/) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694." }, "error": { "login_failed": "PlayStation 4 \uc640 \ud398\uc5b4\ub9c1\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. PIN \uc774 \uc62c\ubc14\ub978\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694.", + "no_ipaddress": "\uad6c\uc131\ud558\uace0\uc790 \ud558\ub294 PlayStation 4 \uc758 IP \uc8fc\uc18c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", "not_ready": "PlayStation 4 \uac00 \ucf1c\uc838 \uc788\uc9c0 \uc54a\uac70\ub098 \ub124\ud2b8\uc6cc\ud06c\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." }, "step": { @@ -23,7 +24,15 @@ "name": "\uc774\ub984", "region": "\uc9c0\uc5ed" }, - "description": "PlayStation 4 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. 'PIN' \uc744 \ud655\uc778\ud558\ub824\uba74, PlayStation 4 \ucf58\uc194\uc5d0\uc11c '\uc124\uc815' \uc73c\ub85c \uc774\ub3d9\ud55c \ub4a4 '\ubaa8\ubc14\uc77c \uc571 \uc811\uc18d \uc124\uc815' \uc73c\ub85c \uc774\ub3d9\ud558\uc5ec '\uae30\uae30 \ub4f1\ub85d\ud558\uae30' \ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \ud654\uba74\uc5d0 \ud45c\uc2dc\ub41c 8\uc790\ub9ac \uc22b\uc790\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", + "description": "PlayStation 4 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. 'PIN' \uc744 \ud655\uc778\ud558\ub824\uba74, PlayStation 4 \ucf58\uc194\uc5d0\uc11c '\uc124\uc815' \uc73c\ub85c \uc774\ub3d9\ud55c \ub4a4 '\ubaa8\ubc14\uc77c \uc571 \uc811\uc18d \uc124\uc815' \uc73c\ub85c \uc774\ub3d9\ud558\uc5ec '\uae30\uae30 \ub4f1\ub85d\ud558\uae30' \ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \ud654\uba74\uc5d0 \ud45c\uc2dc\ub41c 8\uc790\ub9ac \uc22b\uc790\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. \ucd94\uac00 \uc815\ubcf4\ub294 [\uc548\ub0b4](https://www.home-assistant.io/components/ps4/) \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP \uc8fc\uc18c (\uc790\ub3d9 \uac80\uc0c9\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ube44\uc6cc\ub450\uc138\uc694)", + "mode": "\uad6c\uc131 \ubaa8\ub4dc" + }, + "description": "\uad6c\uc131 \ubaa8\ub4dc\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \uc790\ub3d9 \uac80\uc0c9\uc744 \uc120\ud0dd\ud558\uba74 \uae30\uae30\uac00 \uc790\ub3d9\uc73c\ub85c \uac80\uc0c9\ub418\ubbc0\ub85c IP \uc8fc\uc18c \ud544\ub4dc\ub294 \ube44\uc6cc\ub458 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "title": "PlayStation 4" } }, diff --git a/homeassistant/components/ps4/.translations/lb.json b/homeassistant/components/ps4/.translations/lb.json index 15b90cb6b6ba5b..5c5847f28c00df 100644 --- a/homeassistant/components/ps4/.translations/lb.json +++ b/homeassistant/components/ps4/.translations/lb.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "Feeler beim verbanne mat der Playstation 4. Iwwerpr\u00e9ift op de PIN korrekt ass.", + "no_ipaddress": "Gitt d'IP Adresse vun der Playstation 4 an:", "not_ready": "PlayStation 4 ass net un oder mam Netzwierk verbonnen." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Gitt \u00e4r Playstation 4 Informatiounen an. Fir 'PIN', gitt an d'Astellunge vun der Playstation 4 Konsole. Dann op 'Mobile App Verbindungs Astellungen' a wielt \"Apparat dob\u00e4isetzen' aus. Gitt de PIN an deen ugewise g\u00ebtt.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP Address (Eidel loossen falls Auto Discovery benotzt g\u00ebtt)", + "mode": "Konfiguratioun's Modus" + }, + "description": "Konfiguratioun's Modus auswielen. D'Feld IP Adress kann eidel bl\u00e9iwen wann Auto Discovery benotzt g\u00ebtt, well d'Apparaten automatesch entdeckt ginn.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/ru.json b/homeassistant/components/ps4/.translations/ru.json index 41232ddc2d47fb..424d0964729975 100644 --- a/homeassistant/components/ps4/.translations/ru.json +++ b/homeassistant/components/ps4/.translations/ru.json @@ -4,11 +4,12 @@ "credential_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.", "devices_configured": "\u0412\u0441\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b.", "no_devices_found": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 PlayStation 4.", - "port_987_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 987.", - "port_997_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 997." + "port_987_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 987. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/).", + "port_997_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 997. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/)." }, "error": { "login_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 \u0441 PlayStation 4. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e PIN-\u043a\u043e\u0434 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439.", + "no_ipaddress": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 PlayStation 4.", "not_ready": "PlayStation 4 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0438\u043b\u0438 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043a \u0441\u0435\u0442\u0438." }, "step": { @@ -23,7 +24,15 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, - "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**.", + "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](https://www.home-assistant.io/components/ps4/) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", + "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0440\u0435\u0436\u0438\u043c\u0430 \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f)", + "mode": "\u0420\u0435\u0436\u0438\u043c" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u041f\u043e\u043b\u0435 'IP-\u0430\u0434\u0440\u0435\u0441' \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c, \u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043e 'Auto Discovery', \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.", "title": "PlayStation 4" } }, diff --git a/homeassistant/components/ps4/.translations/sl.json b/homeassistant/components/ps4/.translations/sl.json new file mode 100644 index 00000000000000..429a409fb7ec22 --- /dev/null +++ b/homeassistant/components/ps4/.translations/sl.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "credential_error": "Napaka pri pridobivanju poverilnic.", + "devices_configured": "Vse najdene naprave so \u017ee konfigurirane.", + "no_devices_found": "V omre\u017eju ni najdenih naprav PS4.", + "port_987_bind_error": "Ne morem se povezati z vrati 987. Dodatne informacije najdete v [dokumentaciji] (https://www.home-assistant.io/components/ps4/).", + "port_997_bind_error": "Ne morem se povezati z vrati 997. Dodatne informacije najdete v [dokumentaciji] (https://www.home-assistant.io/components/ps4/)." + }, + "error": { + "login_failed": "Neuspelo seznanjanje s PlayStation 4. Preverite, ali je koda PIN pravilna.", + "no_ipaddress": "Vnesite IP naslov PlayStation-a 4, ki ga \u017eelite konfigurirati.", + "not_ready": "PlayStation 4 ni vklopljen ali povezan z omre\u017ejem." + }, + "step": { + "creds": { + "description": "Potrebne so poverilnice. Pritisnite 'Po\u0161lji' in nato v aplikaciji PS4 2nd Screen App, osve\u017eite naprave in izberite napravo 'Home-Assistant' za nadaljevanje.", + "title": "PlayStation 4" + }, + "link": { + "data": { + "code": "PIN", + "ip_address": "IP naslov", + "name": "Ime", + "region": "Regija" + }, + "description": "Vnesite va\u0161e PlayStation 4 podatke. Za 'PIN' pojdite na 'Nastavitve' na konzoli PlayStation 4. Nato se pomaknite do mo\u017enosti \u00bbNastavitve povezave z mobilno aplikacijo\u00ab in izberite \u00bbDodaj napravo\u00ab. Vnesite prikazano kodo PIN. Dodatne informacije najdete v [dokumentaciji] (https://www.home-assistant.io/components/ps4/).", + "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "Naslov IP (Pustite prazno, \u010de uporabljate samodejno odkrivanje).", + "mode": "Na\u010din konfiguracije" + }, + "description": "Izberite na\u010din za konfiguracijo. IP-Naslov, polje lahko pustite prazno, \u010de izberete samodejno odkrivanje, saj bodo naprave samodejno odkrite.", + "title": "PlayStation 4" + } + }, + "title": "PlayStation 4" + } +} \ No newline at end of file diff --git a/homeassistant/components/ps4/.translations/zh-Hans.json b/homeassistant/components/ps4/.translations/zh-Hans.json index 8c975e8170c158..118226354af9e4 100644 --- a/homeassistant/components/ps4/.translations/zh-Hans.json +++ b/homeassistant/components/ps4/.translations/zh-Hans.json @@ -23,6 +23,7 @@ "name": "\u540d\u79f0", "region": "\u5730\u533a" }, + "description": "\u8f93\u5165\u60a8\u7684 PlayStation 4 \u4fe1\u606f\u3002\u5bf9\u4e8e \"PIN\", \u8bf7\u5bfc\u822a\u5230 PlayStation 4 \u63a7\u5236\u53f0\u4e0a\u7684 \"\u8bbe\u7f6e\"\u3002\u7136\u540e\u5bfc\u822a\u5230 \"\u79fb\u52a8\u5e94\u7528\u8fde\u63a5\u8bbe\u7f6e\", \u7136\u540e\u9009\u62e9 \"\u6dfb\u52a0\u8bbe\u5907\"\u3002\u8f93\u5165\u663e\u793a\u7684 PIN\u3002", "title": "PlayStation 4" } }, diff --git a/homeassistant/components/rainmachine/.translations/hu.json b/homeassistant/components/rainmachine/.translations/hu.json index 0f5b6b71126208..d95ec9eaa1b5ee 100644 --- a/homeassistant/components/rainmachine/.translations/hu.json +++ b/homeassistant/components/rainmachine/.translations/hu.json @@ -7,7 +7,7 @@ "step": { "user": { "data": { - "ip_address": "Kiszolg\u00e1l\u00f3 neve vagy IP c\u00edme", + "ip_address": "Hosztn\u00e9v vagy IP c\u00edm", "password": "Jelsz\u00f3", "port": "Port" }, diff --git a/homeassistant/components/rainmachine/.translations/zh-Hans.json b/homeassistant/components/rainmachine/.translations/zh-Hans.json index e7171ca28672b7..f3d8308fabf1d4 100644 --- a/homeassistant/components/rainmachine/.translations/zh-Hans.json +++ b/homeassistant/components/rainmachine/.translations/zh-Hans.json @@ -13,6 +13,7 @@ }, "title": "\u586b\u5199\u60a8\u7684\u4fe1\u606f" } - } + }, + "title": "RainMachine" } } \ No newline at end of file diff --git a/homeassistant/components/season/.translations/sensor.is.json b/homeassistant/components/season/.translations/sensor.is.json new file mode 100644 index 00000000000000..2d48745436b7c1 --- /dev/null +++ b/homeassistant/components/season/.translations/sensor.is.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "Haust", + "spring": "Vor", + "summer": "Sumar", + "winter": "Vetur" + } +} \ No newline at end of file diff --git a/homeassistant/components/smartthings/.translations/sl.json b/homeassistant/components/smartthings/.translations/sl.json index e274d8c939407e..506eb98cc95e37 100644 --- a/homeassistant/components/smartthings/.translations/sl.json +++ b/homeassistant/components/smartthings/.translations/sl.json @@ -7,7 +7,8 @@ "token_already_setup": "\u017deton je \u017ee nastavljen.", "token_forbidden": "\u017deton nima zahtevanih OAuth obsegov.", "token_invalid_format": "\u017deton mora biti v formatu UID / GUID", - "token_unauthorized": "\u017deton ni veljaven ali ni ve\u010d poobla\u0161\u010den." + "token_unauthorized": "\u017deton ni veljaven ali ni ve\u010d poobla\u0161\u010den.", + "webhook_error": "SmartThings ni mogel potrditi kon\u010dne to\u010dke nastavljene v 'base_url`. Prosimo, preglejte zahteve komponente." }, "step": { "user": { diff --git a/homeassistant/components/smartthings/.translations/zh-Hans.json b/homeassistant/components/smartthings/.translations/zh-Hans.json new file mode 100644 index 00000000000000..2326c394cc018c --- /dev/null +++ b/homeassistant/components/smartthings/.translations/zh-Hans.json @@ -0,0 +1,28 @@ +{ + "config": { + "error": { + "app_not_installed": "\u8bf7\u786e\u4fdd\u60a8\u5df2\u5b89\u88c5\u5e76\u6388\u6743 Home Assistant SmartApp\uff0c\u7136\u540e\u518d\u8bd5\u4e00\u6b21\u3002", + "app_setup_error": "\u65e0\u6cd5\u8bbe\u7f6e SmartApp\u3002\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002", + "base_url_not_https": "\u5fc5\u987b\u914d\u7f6e `http` \u7ec4\u4ef6\u7684 `base_url` \u5e76\u4ee5 `https://` \u5f00\u5934\u3002", + "token_already_setup": "\u4ee4\u724c\u5df2\u7ecf\u8bbe\u7f6e\u3002", + "token_forbidden": "\u4ee4\u724c\u6ca1\u6709\u6240\u9700\u7684 OAuth \u4f5c\u7528\u57df\u3002", + "token_invalid_format": "\u4ee4\u724c\u5fc5\u987b\u7b26\u5408 UID/GUID \u683c\u5f0f", + "token_unauthorized": "\u4ee4\u724c\u65e0\u6548\u6216\u5df2\u5931\u6548\u3002", + "webhook_error": "SmartThings \u65e0\u6cd5\u9a8c\u8bc1 `base_url` \u4e2d\u914d\u7f6e\u7684\u7aef\u70b9\u3002\u8bf7\u67e5\u770b\u7ec4\u4ef6\u9700\u6c42\u3002" + }, + "step": { + "user": { + "data": { + "access_token": "\u8bbf\u95ee\u4ee4\u724c" + }, + "description": "\u8bf7\u8f93\u5165\u6309\u7167[\u8bf4\u660e]({component_url})\u521b\u5efa\u7684 SmartThings [\u4e2a\u4eba\u8bbf\u95ee\u4ee4\u724c]({token_url})\u3002", + "title": "\u8f93\u5165\u4e2a\u4eba\u8bbf\u95ee\u4ee4\u724c" + }, + "wait_install": { + "description": "\u8bf7\u81f3\u5c11\u5728\u4e00\u4e2a\u4f4d\u7f6e\u5b89\u88c5 Home Assistant SmartApp\uff0c\u7136\u540e\u70b9\u51fb\u201c\u63d0\u4ea4\u201d\u3002", + "title": "\u5b89\u88c5 SmartApp" + } + }, + "title": "SmartThings" + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/.translations/en.json b/homeassistant/components/tellduslive/.translations/en.json index 4ed9ef597f489d..c2b00561858746 100644 --- a/homeassistant/components/tellduslive/.translations/en.json +++ b/homeassistant/components/tellduslive/.translations/en.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "all_configured": "TelldusLive is already configured", "already_setup": "TelldusLive is already configured", "authorize_url_fail": "Unknown error generating an authorize url.", "authorize_url_timeout": "Timeout generating authorize url.", diff --git a/homeassistant/components/tellduslive/.translations/hu.json b/homeassistant/components/tellduslive/.translations/hu.json index 6057d7b3212df8..cd219be04e1ff1 100644 --- a/homeassistant/components/tellduslive/.translations/hu.json +++ b/homeassistant/components/tellduslive/.translations/hu.json @@ -13,7 +13,7 @@ "step": { "user": { "data": { - "host": "Kiszolg\u00e1l\u00f3" + "host": "Hoszt" }, "description": "\u00dcres", "title": "V\u00e1lassz v\u00e9gpontot." diff --git a/homeassistant/components/tellduslive/.translations/ru.json b/homeassistant/components/tellduslive/.translations/ru.json index 80dff6dc88acef..3b34e048b11094 100644 --- a/homeassistant/components/tellduslive/.translations/ru.json +++ b/homeassistant/components/tellduslive/.translations/ru.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "all_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", - "already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "all_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", + "already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "authorize_url_fail": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430" diff --git a/homeassistant/components/tellduslive/.translations/zh-Hans.json b/homeassistant/components/tellduslive/.translations/zh-Hans.json index 4b1afd548e847e..bcf36cafda07dc 100644 --- a/homeassistant/components/tellduslive/.translations/zh-Hans.json +++ b/homeassistant/components/tellduslive/.translations/zh-Hans.json @@ -2,6 +2,7 @@ "config": { "abort": { "all_configured": "Tellduslive \u5df2\u914d\u7f6e\u5b8c\u6210", + "already_setup": "TelldusLive \u5df2\u914d\u7f6e\u5b8c\u6210", "authorize_url_fail": "\u751f\u6210\u6388\u6743\u7f51\u5740\u65f6\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002", "authorize_url_timeout": "\u751f\u6210\u6388\u6743\u7f51\u5740\u8d85\u65f6\u3002", "unknown": "\u53d1\u751f\u672a\u77e5\u7684\u9519\u8bef" @@ -11,13 +12,16 @@ }, "step": { "auth": { - "description": "\u8981\u94fe\u63a5\u60a8\u7684TelldusLive\u8d26\u6237\uff1a \n 1. \u70b9\u51fb\u4e0b\u9762\u7684\u94fe\u63a5\n 2. \u767b\u5f55 Telldus Live \n 3. \u6388\u6743 **{app_name}** (\u70b9\u51fb **\u662f**)\u3002 \n 4. \u8fd4\u56de\u6b64\u9875\uff0c\u7136\u540e\u70b9\u51fb**\u63d0\u4ea4**\u3002 \n\n [\u94fe\u63a5 TelldusLive \u8d26\u6237]({auth_url})" + "description": "\u8981\u94fe\u63a5\u60a8\u7684TelldusLive\u8d26\u6237\uff1a \n 1. \u70b9\u51fb\u4e0b\u9762\u7684\u94fe\u63a5\n 2. \u767b\u5f55 Telldus Live \n 3. \u6388\u6743 **{app_name}** (\u70b9\u51fb **\u662f**)\u3002 \n 4. \u8fd4\u56de\u6b64\u9875\uff0c\u7136\u540e\u70b9\u51fb**\u63d0\u4ea4**\u3002 \n\n [\u94fe\u63a5 TelldusLive \u8d26\u6237]({auth_url})", + "title": "\u4f7f\u7528 TelldusLive \u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1" }, "user": { "data": { "host": "\u4e3b\u673a" - } + }, + "title": "\u9009\u62e9 endpoint\u3002" } - } + }, + "title": "Telldus Live" } } \ No newline at end of file diff --git a/homeassistant/components/toon/.translations/sl.json b/homeassistant/components/toon/.translations/sl.json new file mode 100644 index 00000000000000..18c1a739e5ab75 --- /dev/null +++ b/homeassistant/components/toon/.translations/sl.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "client_id": "ID odjemalca iz konfiguracije je neveljaven.", + "client_secret": "Skrivnost iz konfiguracije odjemalca ni veljaven.", + "no_agreements": "Ta ra\u010dun nima prikazov Toon.", + "no_app": "Toon morate konfigurirati, preden ga boste lahko uporabili za overitev. [Preberite navodila] (https://www.home-assistant.io/components/toon/).", + "unknown_auth_fail": "Pri preverjanju pristnosti je pri\u0161lo do nepri\u010dakovane napake." + }, + "error": { + "credentials": "Navedene poverilnice niso veljavne.", + "display_exists": "Izbrani zaslon je \u017ee konfiguriran." + }, + "step": { + "authenticate": { + "data": { + "password": "Geslo", + "tenant": "Najemnik", + "username": "Uporabni\u0161ko ime" + }, + "description": "Prijavite se s svojim Eneco toon ra\u010dunom (ne razvijalskim).", + "title": "Pove\u017eite svoj Toon ra\u010dun" + }, + "display": { + "data": { + "display": "Izberite zaslon" + }, + "description": "Izberite zaslon Toon, s katerim se \u017eelite povezati.", + "title": "Izberite zaslon" + } + }, + "title": "Toon" + } +} \ No newline at end of file diff --git a/homeassistant/components/tplink/.translations/sl.json b/homeassistant/components/tplink/.translations/sl.json new file mode 100644 index 00000000000000..e686ee4bc04165 --- /dev/null +++ b/homeassistant/components/tplink/.translations/sl.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "TP-Link naprav ni mogo\u010de najti v omre\u017eju.", + "single_instance_allowed": "Potrebna je samo ena konfiguracija." + }, + "step": { + "confirm": { + "description": "\u017delite namestiti pametne naprave TP-Link?", + "title": "TP-Link Pametni Dom" + } + }, + "title": "TP-Link Pametni Dom" + } +} \ No newline at end of file diff --git a/homeassistant/components/tplink/.translations/zh-Hans.json b/homeassistant/components/tplink/.translations/zh-Hans.json new file mode 100644 index 00000000000000..ca3ac91337553b --- /dev/null +++ b/homeassistant/components/tplink/.translations/zh-Hans.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230 TP-Link \u8bbe\u5907\u3002", + "single_instance_allowed": "\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002" + }, + "step": { + "confirm": { + "description": "\u60a8\u60f3\u8981\u914d\u7f6e TP-Link \u667a\u80fd\u8bbe\u5907\u5417\uff1f", + "title": "TP-Link Smart Home" + } + }, + "title": "TP-Link Smart Home" + } +} \ No newline at end of file diff --git a/homeassistant/components/tradfri/.translations/ru.json b/homeassistant/components/tradfri/.translations/ru.json index c42ca6b7b2b3f8..352579f810cebd 100644 --- a/homeassistant/components/tradfri/.translations/ru.json +++ b/homeassistant/components/tradfri/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0428\u043b\u044e\u0437 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d" + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430" }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0448\u043b\u044e\u0437\u0443", diff --git a/homeassistant/components/unifi/.translations/hu.json b/homeassistant/components/unifi/.translations/hu.json index 6f78beaffd631d..b927e652ba7906 100644 --- a/homeassistant/components/unifi/.translations/hu.json +++ b/homeassistant/components/unifi/.translations/hu.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "host": "Host", + "host": "Hoszt", "password": "Jelsz\u00f3", "port": "Port", "site": "Site azonos\u00edt\u00f3", diff --git a/homeassistant/components/unifi/.translations/ru.json b/homeassistant/components/unifi/.translations/ru.json index 381f4831c536c1..c061ab36e7bd55 100644 --- a/homeassistant/components/unifi/.translations/ru.json +++ b/homeassistant/components/unifi/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "user_privilege": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c" }, "error": { diff --git a/homeassistant/components/unifi/.translations/zh-Hans.json b/homeassistant/components/unifi/.translations/zh-Hans.json index c8796536e2f0bf..80ed9eb2fa5e32 100644 --- a/homeassistant/components/unifi/.translations/zh-Hans.json +++ b/homeassistant/components/unifi/.translations/zh-Hans.json @@ -5,6 +5,7 @@ "user_privilege": "\u7528\u6237\u987b\u4e3a\u7ba1\u7406\u5458" }, "error": { + "faulty_credentials": "\u9519\u8bef\u7684\u7528\u6237\u51ed\u636e", "service_unavailable": "\u6ca1\u6709\u53ef\u7528\u7684\u670d\u52a1" }, "step": { diff --git a/homeassistant/components/upnp/.translations/en.json b/homeassistant/components/upnp/.translations/en.json index 91e4f6b7c52574..632d5112f1ae2e 100644 --- a/homeassistant/components/upnp/.translations/en.json +++ b/homeassistant/components/upnp/.translations/en.json @@ -1,13 +1,28 @@ { "config": { "abort": { + "already_configured": "UPnP/IGD is already configured", + "incomplete_device": "Ignoring incomplete UPnP device", + "no_devices_discovered": "No UPnP/IGDs discovered", "no_devices_found": "No UPnP/IGD devices found on the network.", + "no_sensors_or_port_mapping": "Enable at least sensors or port mapping", "single_instance_allowed": "Only a single configuration of UPnP/IGD is necessary." }, "step": { "confirm": { "description": "Do you want to set up UPnP/IGD?", "title": "UPnP/IGD" + }, + "init": { + "title": "UPnP/IGD" + }, + "user": { + "data": { + "enable_port_mapping": "Enable port mapping for Home Assistant", + "enable_sensors": "Add traffic sensors", + "igd": "UPnP/IGD" + }, + "title": "Configuration options for the UPnP/IGD" } }, "title": "UPnP/IGD" diff --git a/homeassistant/components/upnp/.translations/hu.json b/homeassistant/components/upnp/.translations/hu.json index 7d3827e76da6ea..29dab5e09da0d7 100644 --- a/homeassistant/components/upnp/.translations/hu.json +++ b/homeassistant/components/upnp/.translations/hu.json @@ -13,7 +13,7 @@ }, "step": { "confirm": { - "description": "Be akarja \u00e1ll\u00edtani a UPnP/IGD-t?", + "description": "Be szeretn\u00e9d \u00e1ll\u00edtani a UPnP/IGD-t?", "title": "UPnP/IGD" }, "init": { diff --git a/homeassistant/components/upnp/.translations/ru.json b/homeassistant/components/upnp/.translations/ru.json index 6a7c43f9e464f1..668b9a377fc18b 100644 --- a/homeassistant/components/upnp/.translations/ru.json +++ b/homeassistant/components/upnp/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "incomplete_device": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435\u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 UPnP", "no_devices_discovered": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e UPnP / IGD", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 UPnP / IGD \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", diff --git a/homeassistant/components/zha/.translations/en.json b/homeassistant/components/zha/.translations/en.json index 82489ac258ec14..f0da251f5eb643 100644 --- a/homeassistant/components/zha/.translations/en.json +++ b/homeassistant/components/zha/.translations/en.json @@ -12,7 +12,6 @@ "radio_type": "Radio Type", "usb_path": "USB Device Path" }, - "description": "", "title": "ZHA" } }, diff --git a/homeassistant/components/zha/.translations/zh-Hans.json b/homeassistant/components/zha/.translations/zh-Hans.json index ce458fa32f1304..2c81c60318673a 100644 --- a/homeassistant/components/zha/.translations/zh-Hans.json +++ b/homeassistant/components/zha/.translations/zh-Hans.json @@ -9,6 +9,7 @@ "step": { "user": { "data": { + "radio_type": "\u65e0\u7ebf\u7535\u7c7b\u578b", "usb_path": "USB \u8bbe\u5907\u8def\u5f84" }, "description": "\u7a7a\u767d", diff --git a/homeassistant/components/zwave/.translations/ru.json b/homeassistant/components/zwave/.translations/ru.json index b6856e4590ace9..a64b4db185d7a1 100644 --- a/homeassistant/components/zwave/.translations/ru.json +++ b/homeassistant/components/zwave/.translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430", "one_instance_only": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u043e\u0434\u043d\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c Z-Wave" }, "error": { From 26726af689cc9837d8686a3c90bba56265e06f9d Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Thu, 28 Mar 2019 00:47:07 -0400 Subject: [PATCH 221/605] Stream Record Service (#22456) * Initial commit of record service for live streams * fix lint * update service descriptions * add tests * fix lint --- homeassistant/components/camera/__init__.py | 39 ++++++- homeassistant/components/camera/services.yaml | 16 +++ homeassistant/components/stream/__init__.py | 81 ++++++++++++-- homeassistant/components/stream/const.py | 6 + homeassistant/components/stream/core.py | 23 ++-- homeassistant/components/stream/hls.py | 5 + homeassistant/components/stream/recorder.py | 92 ++++++++++++++++ homeassistant/components/stream/worker.py | 2 +- tests/components/camera/test_init.py | 35 ++++++ tests/components/stream/test_init.py | 103 ++++++++++++++++++ tests/components/stream/test_recorder.py | 83 ++++++++++++++ 11 files changed, 466 insertions(+), 19 deletions(-) create mode 100644 homeassistant/components/stream/recorder.py create mode 100644 tests/components/stream/test_init.py create mode 100644 tests/components/stream/test_recorder.py diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index cdd8a844389a54..e453cdfd1a166a 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -20,7 +20,7 @@ from homeassistant.core import callback from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, \ - SERVICE_TURN_ON, EVENT_HOMEASSISTANT_START + SERVICE_TURN_ON, EVENT_HOMEASSISTANT_START, CONF_FILENAME from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import bind_hass from homeassistant.helpers.entity import Entity @@ -33,7 +33,8 @@ SERVICE_PLAY_MEDIA, DOMAIN as DOMAIN_MP) from homeassistant.components.stream import request_stream from homeassistant.components.stream.const import ( - OUTPUT_FORMATS, FORMAT_CONTENT_TYPE) + OUTPUT_FORMATS, FORMAT_CONTENT_TYPE, CONF_STREAM_SOURCE, CONF_LOOKBACK, + CONF_DURATION, SERVICE_RECORD, DOMAIN as DOMAIN_STREAM) from homeassistant.components import websocket_api import homeassistant.helpers.config_validation as cv @@ -85,6 +86,12 @@ vol.Optional(ATTR_FORMAT, default='hls'): vol.In(OUTPUT_FORMATS), }) +CAMERA_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({ + vol.Required(CONF_FILENAME): cv.template, + vol.Optional(CONF_DURATION, default=30): int, + vol.Optional(CONF_LOOKBACK, default=0): int, +}) + WS_TYPE_CAMERA_THUMBNAIL = 'camera_thumbnail' SCHEMA_WS_CAMERA_THUMBNAIL = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ vol.Required('type'): WS_TYPE_CAMERA_THUMBNAIL, @@ -260,6 +267,10 @@ def update_tokens(time): SERVICE_PLAY_STREAM, CAMERA_SERVICE_PLAY_STREAM, async_handle_play_stream_service ) + component.async_register_entity_service( + SERVICE_RECORD, CAMERA_SERVICE_RECORD, + async_handle_record_service + ) return True @@ -640,3 +651,27 @@ async def async_handle_play_stream_service(camera, service_call): await hass.services.async_call( DOMAIN_MP, SERVICE_PLAY_MEDIA, data, blocking=True, context=service_call.context) + + +async def async_handle_record_service(camera, call): + """Handle stream recording service calls.""" + if not camera.stream_source: + raise HomeAssistantError("{} does not support record service" + .format(camera.entity_id)) + + hass = camera.hass + filename = call.data[CONF_FILENAME] + filename.hass = hass + video_path = filename.async_render( + variables={ATTR_ENTITY_ID: camera}) + + data = { + CONF_STREAM_SOURCE: camera.stream_source, + CONF_FILENAME: video_path, + CONF_DURATION: call.data[CONF_DURATION], + CONF_LOOKBACK: call.data[CONF_LOOKBACK], + } + + await hass.services.async_call( + DOMAIN_STREAM, SERVICE_RECORD, data, + blocking=True, context=call.context) diff --git a/homeassistant/components/camera/services.yaml b/homeassistant/components/camera/services.yaml index 575f1fe76f7d1f..45a0f4cfec0963 100644 --- a/homeassistant/components/camera/services.yaml +++ b/homeassistant/components/camera/services.yaml @@ -51,6 +51,22 @@ play_stream: description: (Optional) Stream format supported by media player. example: 'hls' +record: + description: Record live camera feed. + fields: + entity_id: + description: Name of entities to record. + example: 'camera.living_room_camera' + filename: + description: Template of a Filename. Variable is entity_id. Must be mp4. + example: '/tmp/snapshot_{{ entity_id }}.mp4' + duration: + description: (Optional) Target recording length (in seconds). Default: 30 + example: 30 + lookback: + description: (Optional) Target lookback period (in seconds) to include in addition to duration. Only available if there is currently an active HLS stream. + example: 4 + local_file_update_file_path: description: Update the file_path for a local_file camera. fields: diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index a68f1c47dbf413..1e8ae5d60e3be8 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -10,15 +10,19 @@ import voluptuous as vol from homeassistant.auth.util import generate_secret -from homeassistant.const import EVENT_HOMEASSISTANT_STOP +import homeassistant.helpers.config_validation as cv +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_FILENAME from homeassistant.core import callback from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import bind_hass -from .const import DOMAIN, ATTR_STREAMS, ATTR_ENDPOINTS +from .const import ( + DOMAIN, ATTR_STREAMS, ATTR_ENDPOINTS, CONF_STREAM_SOURCE, + CONF_DURATION, CONF_LOOKBACK, SERVICE_RECORD) from .core import PROVIDERS from .worker import stream_worker from .hls import async_setup_hls +from .recorder import async_setup_recorder REQUIREMENTS = ['av==6.1.2'] @@ -30,6 +34,16 @@ DOMAIN: vol.Schema({}), }, extra=vol.ALLOW_EXTRA) +STREAM_SERVICE_SCHEMA = vol.Schema({ + vol.Required(CONF_STREAM_SOURCE): cv.string, +}) + +SERVICE_RECORD_SCHEMA = STREAM_SERVICE_SCHEMA.extend({ + vol.Required(CONF_FILENAME): cv.string, + vol.Optional(CONF_DURATION, default=30): int, + vol.Optional(CONF_LOOKBACK, default=0): int, +}) + # Set log level to error for libav logging.getLogger('libav').setLevel(logging.ERROR) @@ -82,6 +96,9 @@ async def async_setup(hass, config): hls_endpoint = async_setup_hls(hass) hass.data[DOMAIN][ATTR_ENDPOINTS]['hls'] = hls_endpoint + # Setup Recorder + async_setup_recorder(hass) + @callback def shutdown(event): """Stop all stream workers.""" @@ -92,6 +109,13 @@ def shutdown(event): hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shutdown) + async def async_record(call): + """Call record stream service handler.""" + await async_handle_record_service(hass, call) + + hass.services.async_register(DOMAIN, SERVICE_RECORD, + async_record, schema=SERVICE_RECORD_SCHEMA) + return True @@ -119,15 +143,15 @@ def outputs(self): def add_provider(self, fmt): """Add provider output stream.""" - provider = PROVIDERS[fmt](self) - if not self._outputs.get(provider.format): - self._outputs[provider.format] = provider - return self._outputs[provider.format] + if not self._outputs.get(fmt): + provider = PROVIDERS[fmt](self) + self._outputs[fmt] = provider + return self._outputs[fmt] def remove_provider(self, provider): """Remove provider output stream.""" - if provider.format in self._outputs: - del self._outputs[provider.format] + if provider.name in self._outputs: + del self._outputs[provider.name] self.check_idle() if not self._outputs: @@ -165,3 +189,44 @@ def _stop(self): self._thread.join() self._thread = None _LOGGER.info("Stopped stream: %s", self.source) + + +async def async_handle_record_service(hass, call): + """Handle save video service calls.""" + stream_source = call.data[CONF_STREAM_SOURCE] + video_path = call.data[CONF_FILENAME] + duration = call.data[CONF_DURATION] + lookback = call.data[CONF_LOOKBACK] + + # Check for file access + if not hass.config.is_allowed_path(video_path): + raise HomeAssistantError("Can't write {}, no access to path!" + .format(video_path)) + + # Check for active stream + streams = hass.data[DOMAIN][ATTR_STREAMS] + stream = streams.get(stream_source) + if not stream: + stream = Stream(hass, stream_source) + streams[stream_source] = stream + + # Add recorder + recorder = stream.outputs.get('recorder') + if recorder: + raise HomeAssistantError("Stream already recording to {}!" + .format(recorder.video_path)) + + recorder = stream.add_provider('recorder') + recorder.video_path = video_path + recorder.timeout = duration + + stream.start() + + # Take advantage of lookback + hls = stream.outputs.get('hls') + if lookback > 0 and hls: + num_segments = min(int(lookback // hls.target_duration), + hls.num_segments) + # Wait for latest segment, then add the lookback + await hls.recv() + recorder.prepend(list(hls.get_segment())[-num_segments:]) diff --git a/homeassistant/components/stream/const.py b/homeassistant/components/stream/const.py index a87daaa9d4001d..9421faaff9a902 100644 --- a/homeassistant/components/stream/const.py +++ b/homeassistant/components/stream/const.py @@ -1,10 +1,16 @@ """Constants for Stream component.""" DOMAIN = 'stream' +CONF_STREAM_SOURCE = 'stream_source' +CONF_LOOKBACK = 'lookback' +CONF_DURATION = 'duration' + ATTR_ENDPOINTS = 'endpoints' ATTR_STREAMS = 'streams' ATTR_KEEPALIVE = 'keepalive' +SERVICE_RECORD = 'record' + OUTPUT_FORMATS = ['hls'] FORMAT_CONTENT_TYPE = { diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 59c0a6b650fd46..745c334fce00fe 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -41,15 +41,21 @@ class StreamOutput: num_segments = 3 - def __init__(self, stream) -> None: + def __init__(self, stream, timeout: int = 300) -> None: """Initialize a stream output.""" self.idle = False + self.timeout = timeout self._stream = stream self._cursor = None self._event = asyncio.Event() self._segments = deque(maxlen=self.num_segments) self._unsub = None + @property + def name(self) -> str: + """Return provider name.""" + return None + @property def format(self) -> str: """Return container format.""" @@ -82,7 +88,8 @@ def get_segment(self, sequence: int = None) -> Any: # Reset idle timeout if self._unsub is not None: self._unsub() - self._unsub = async_call_later(self._stream.hass, 300, self._timeout) + self._unsub = async_call_later( + self._stream.hass, self.timeout, self._timeout) if not sequence: return self._segments @@ -111,14 +118,14 @@ def put(self, segment: Segment) -> None: # Start idle timeout when we start recieving data if self._unsub is None: self._unsub = async_call_later( - self._stream.hass, 300, self._timeout) + self._stream.hass, self.timeout, self._timeout) if segment is None: self._event.set() # Cleanup provider if self._unsub is not None: self._unsub() - self._cleanup() + self.cleanup() return self._segments.append(segment) @@ -133,11 +140,11 @@ def _timeout(self, _now=None): self.idle = True self._stream.check_idle() else: - self._cleanup() + self.cleanup() - def _cleanup(self): - """Remove provider.""" - self._segments = [] + def cleanup(self): + """Handle cleanup.""" + self._segments = deque(maxlen=self.num_segments) self._stream.remove_provider(self) diff --git a/homeassistant/components/stream/hls.py b/homeassistant/components/stream/hls.py index 8f5dd6c18845a6..aa5ce1057643d6 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -110,6 +110,11 @@ def render(self, track, start_time): class HlsStreamOutput(StreamOutput): """Represents HLS Output formats.""" + @property + def name(self) -> str: + """Return provider name.""" + return 'hls' + @property def format(self) -> str: """Return container format.""" diff --git a/homeassistant/components/stream/recorder.py b/homeassistant/components/stream/recorder.py new file mode 100644 index 00000000000000..15e2108c82afbc --- /dev/null +++ b/homeassistant/components/stream/recorder.py @@ -0,0 +1,92 @@ +"""Provide functionality to record stream.""" +import threading +from typing import List + +from homeassistant.core import callback + +from .core import Segment, StreamOutput, PROVIDERS + + +@callback +def async_setup_recorder(hass): + """Only here so Provider Registry works.""" + + +def recorder_save_worker(file_out: str, segments: List[Segment]): + """Handle saving stream.""" + import av + + output = av.open(file_out, 'w', options={'movflags': 'frag_keyframe'}) + output_v = None + + for segment in segments: + # Seek to beginning and open segment + segment.segment.seek(0) + source = av.open(segment.segment, 'r', format='mpegts') + source_v = source.streams.video[0] + + # Add output streams + if not output_v: + output_v = output.add_stream(template=source_v) + + # Remux video + for packet in source.demux(source_v): + if packet is not None and packet.dts is not None: + packet.stream = output_v + output.mux(packet) + + output.close() + + +@PROVIDERS.register('recorder') +class RecorderOutput(StreamOutput): + """Represents HLS Output formats.""" + + def __init__(self, stream, timeout: int = 30) -> None: + """Initialize recorder output.""" + super().__init__(stream, timeout) + self.video_path = None + self._segments = [] + + @property + def name(self) -> str: + """Return provider name.""" + return 'recorder' + + @property + def format(self) -> str: + """Return container format.""" + return 'mpegts' + + @property + def audio_codec(self) -> str: + """Return desired audio codec.""" + return 'aac' + + @property + def video_codec(self) -> str: + """Return desired video codec.""" + return 'h264' + + def prepend(self, segments: List[Segment]) -> None: + """Prepend segments to existing list.""" + own_segments = self.segments + segments = [s for s in segments if s.sequence not in own_segments] + self._segments = segments + self._segments + + @callback + def _timeout(self, _now=None): + """Handle recorder timeout.""" + self._unsub = None + self.cleanup() + + def cleanup(self): + """Write recording and clean up.""" + thread = threading.Thread( + name='recorder_save_worker', + target=recorder_save_worker, + args=(self.video_path, self._segments)) + thread.start() + + self._segments = [] + self._stream.remove_provider(self) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index d0196761968b38..3ca8ac079e350d 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -112,7 +112,7 @@ def stream_worker(hass, stream, quit_event): a_packet, buffer = create_stream_buffer( stream_output, video_stream, audio_frame) audio_packets[buffer.astream] = a_packet - outputs[stream_output.format] = buffer + outputs[stream_output.name] = buffer # First video packet tends to have a weird dts/pts if first_packet: diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 701a368283083a..e730f39656ed91 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -341,3 +341,38 @@ async def test_preload_stream(hass, mock_stream): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() assert mock_request_stream.called + + +async def test_record_service_invalid_path(hass, mock_camera): + """Test record service with invalid path.""" + data = { + ATTR_ENTITY_ID: 'camera.demo_camera', + camera.CONF_FILENAME: '/my/invalid/path' + } + with patch.object(hass.config, 'is_allowed_path', return_value=False), \ + pytest.raises(HomeAssistantError): + # Call service + await hass.services.async_call( + camera.DOMAIN, camera.SERVICE_RECORD, data, blocking=True) + + +async def test_record_service(hass, mock_camera, mock_stream): + """Test record service.""" + data = { + ATTR_ENTITY_ID: 'camera.demo_camera', + camera.CONF_FILENAME: '/my/path' + } + + with patch('homeassistant.components.demo.camera.DemoCamera.stream_source', + new_callable=PropertyMock) as mock_stream_source, \ + patch( + 'homeassistant.components.stream.async_handle_record_service', + return_value=mock_coro()) as mock_record_service, \ + patch.object(hass.config, 'is_allowed_path', return_value=True): + mock_stream_source.return_value = io.BytesIO() + # Call service + await hass.services.async_call( + camera.DOMAIN, camera.SERVICE_RECORD, data, blocking=True) + # So long as we call stream.record, the rest should be covered + # by those tests. + assert mock_record_service.called diff --git a/tests/components/stream/test_init.py b/tests/components/stream/test_init.py new file mode 100644 index 00000000000000..7f68bf1e7bf49f --- /dev/null +++ b/tests/components/stream/test_init.py @@ -0,0 +1,103 @@ +"""The tests for stream.""" +from unittest.mock import patch, MagicMock + +import pytest + +from homeassistant.const import CONF_FILENAME +from homeassistant.components.stream.const import ( + DOMAIN, SERVICE_RECORD, CONF_STREAM_SOURCE, CONF_LOOKBACK, ATTR_STREAMS) +from homeassistant.exceptions import HomeAssistantError +from homeassistant.setup import async_setup_component + +from tests.common import mock_coro + + +async def test_record_service_invalid_file(hass): + """Test record service call with invalid file.""" + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + data = { + CONF_STREAM_SOURCE: 'rtsp://my.video', + CONF_FILENAME: '/my/invalid/path' + } + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + DOMAIN, SERVICE_RECORD, data, blocking=True) + + +async def test_record_service_init_stream(hass): + """Test record service call with invalid file.""" + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + data = { + CONF_STREAM_SOURCE: 'rtsp://my.video', + CONF_FILENAME: '/my/invalid/path' + } + with patch('homeassistant.components.stream.Stream') as stream_mock, \ + patch.object(hass.config, 'is_allowed_path', return_value=True): + # Setup stubs + stream_mock.return_value.outputs = {} + + # Call Service + await hass.services.async_call( + DOMAIN, SERVICE_RECORD, data, blocking=True) + + # Assert + assert stream_mock.called + + +async def test_record_service_existing_record_session(hass): + """Test record service call with invalid file.""" + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + source = 'rtsp://my.video' + data = { + CONF_STREAM_SOURCE: source, + CONF_FILENAME: '/my/invalid/path' + } + + # Setup stubs + stream_mock = MagicMock() + stream_mock.return_value.outputs = {'recorder': MagicMock()} + hass.data[DOMAIN][ATTR_STREAMS][source] = stream_mock + + with patch.object(hass.config, 'is_allowed_path', return_value=True), \ + pytest.raises(HomeAssistantError): + # Call Service + await hass.services.async_call( + DOMAIN, SERVICE_RECORD, data, blocking=True) + + +async def test_record_service_lookback(hass): + """Test record service call with invalid file.""" + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + data = { + CONF_STREAM_SOURCE: 'rtsp://my.video', + CONF_FILENAME: '/my/invalid/path', + CONF_LOOKBACK: 4 + } + + with patch('homeassistant.components.stream.Stream') as stream_mock, \ + patch.object(hass.config, 'is_allowed_path', return_value=True): + # Setup stubs + hls_mock = MagicMock() + hls_mock.num_segments = 3 + hls_mock.target_duration = 2 + hls_mock.recv.return_value = mock_coro() + stream_mock.return_value.outputs = { + 'hls': hls_mock + } + + # Call Service + await hass.services.async_call( + DOMAIN, SERVICE_RECORD, data, blocking=True) + + assert stream_mock.called + stream_mock.return_value.add_provider.assert_called_once_with( + 'recorder') + assert hls_mock.recv.called diff --git a/tests/components/stream/test_recorder.py b/tests/components/stream/test_recorder.py new file mode 100644 index 00000000000000..4e227e463b422d --- /dev/null +++ b/tests/components/stream/test_recorder.py @@ -0,0 +1,83 @@ +"""The tests for hls streams.""" +from datetime import timedelta +from io import BytesIO +from unittest.mock import patch + +from homeassistant.setup import async_setup_component +from homeassistant.components.stream.core import Segment +from homeassistant.components.stream.recorder import recorder_save_worker +import homeassistant.util.dt as dt_util + +from tests.common import async_fire_time_changed +from tests.components.stream.common import ( + generate_h264_video, preload_stream) + + +async def test_record_stream(hass, hass_client): + """ + Test record stream. + + Purposefully not mocking anything here to test full + integration with the stream component. + """ + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + + with patch( + 'homeassistant.components.stream.recorder.recorder_save_worker'): + # Setup demo track + source = generate_h264_video() + stream = preload_stream(hass, source) + recorder = stream.add_provider('recorder') + stream.start() + + segments = 0 + while True: + segment = await recorder.recv() + if not segment: + break + segments += 1 + + stream.stop() + + assert segments == 3 + + +async def test_recorder_timeout(hass, hass_client): + """Test recorder timeout.""" + await async_setup_component(hass, 'stream', { + 'stream': {} + }) + + with patch( + 'homeassistant.components.stream.recorder.RecorderOutput.cleanup' + ) as mock_cleanup: + # Setup demo track + source = generate_h264_video() + stream = preload_stream(hass, source) + recorder = stream.add_provider('recorder') + stream.start() + + await recorder.recv() + + # Wait a minute + future = dt_util.utcnow() + timedelta(minutes=1) + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + assert mock_cleanup.called + + +async def test_recorder_save(): + """Test recorder save.""" + # Setup + source = generate_h264_video() + output = BytesIO() + output.name = 'test.mp4' + + # Run + recorder_save_worker(output, [Segment(1, source, 4)]) + + # Assert + assert output.getvalue() From 6ba28916049e0eaa941756d6783cfe0289955fe4 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 27 Mar 2019 21:53:11 -0700 Subject: [PATCH 222/605] Add trusted_users in trusted networks auth provider (#22478) --- .../auth/providers/trusted_networks.py | 65 +++++- homeassistant/components/auth/login_flow.py | 12 +- homeassistant/helpers/config_validation.py | 15 ++ tests/auth/providers/test_trusted_networks.py | 216 +++++++++++++++++- tests/helpers/test_config_validation.py | 22 ++ 5 files changed, 318 insertions(+), 12 deletions(-) diff --git a/homeassistant/auth/providers/trusted_networks.py b/homeassistant/auth/providers/trusted_networks.py index d0bc45c326a1ad..e8161a2bfb64d2 100644 --- a/homeassistant/auth/providers/trusted_networks.py +++ b/homeassistant/auth/providers/trusted_networks.py @@ -18,8 +18,26 @@ IPAddress = Union[IPv4Address, IPv6Address] IPNetwork = Union[IPv4Network, IPv6Network] +CONF_TRUSTED_NETWORKS = 'trusted_networks' +CONF_TRUSTED_USERS = 'trusted_users' +CONF_GROUP = 'group' +CONF_ALLOW_BYPASS_LOGIN = 'allow_bypass_login' + CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({ - vol.Required('trusted_networks'): vol.All(cv.ensure_list, [ip_network]) + vol.Required(CONF_TRUSTED_NETWORKS): vol.All( + cv.ensure_list, [ip_network] + ), + vol.Optional(CONF_TRUSTED_USERS, default={}): vol.Schema( + # we only validate the format of user_id or group_id + {ip_network: vol.All( + cv.ensure_list, + [vol.Or( + cv.uuid4_hex, + vol.Schema({vol.Required(CONF_GROUP): cv.uuid4_hex}), + )], + )} + ), + vol.Optional(CONF_ALLOW_BYPASS_LOGIN, default=False): cv.boolean, }, extra=vol.PREVENT_EXTRA) @@ -43,7 +61,12 @@ class TrustedNetworksAuthProvider(AuthProvider): @property def trusted_networks(self) -> List[IPNetwork]: """Return trusted networks.""" - return cast(List[IPNetwork], self.config['trusted_networks']) + return cast(List[IPNetwork], self.config[CONF_TRUSTED_NETWORKS]) + + @property + def trusted_users(self) -> Dict[IPNetwork, Any]: + """Return trusted users per network.""" + return cast(Dict[IPNetwork, Any], self.config[CONF_TRUSTED_USERS]) @property def support_mfa(self) -> bool: @@ -53,13 +76,34 @@ def support_mfa(self) -> bool: async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow: """Return a flow to login.""" assert context is not None + ip_addr = cast(IPAddress, context.get('ip_address')) users = await self.store.async_get_users() - available_users = {user.id: user.name - for user in users - if not user.system_generated and user.is_active} + available_users = [user for user in users + if not user.system_generated and user.is_active] + for ip_net, user_or_group_list in self.trusted_users.items(): + if ip_addr in ip_net: + user_list = [user_id for user_id in user_or_group_list + if isinstance(user_id, str)] + group_list = [group[CONF_GROUP] for group in user_or_group_list + if isinstance(group, dict)] + flattened_group_list = [group for sublist in group_list + for group in sublist] + available_users = [ + user for user in available_users + if (user.id in user_list or + any([group.id in flattened_group_list + for group in user.groups])) + ] + break return TrustedNetworksLoginFlow( - self, cast(IPAddress, context.get('ip_address')), available_users) + self, + ip_addr, + { + user.id: user.name for user in available_users + }, + self.config[CONF_ALLOW_BYPASS_LOGIN], + ) async def async_get_or_create_credentials( self, flow_result: Dict[str, str]) -> Credentials: @@ -109,11 +153,13 @@ class TrustedNetworksLoginFlow(LoginFlow): def __init__(self, auth_provider: TrustedNetworksAuthProvider, ip_addr: IPAddress, - available_users: Dict[str, Optional[str]]) -> None: + available_users: Dict[str, Optional[str]], + allow_bypass_login: bool) -> None: """Initialize the login flow.""" super().__init__(auth_provider) self._available_users = available_users self._ip_address = ip_addr + self._allow_bypass_login = allow_bypass_login async def async_step_init( self, user_input: Optional[Dict[str, str]] = None) \ @@ -131,6 +177,11 @@ async def async_step_init( if user_input is not None: return await self.async_finish(user_input) + if self._allow_bypass_login and len(self._available_users) == 1: + return await self.async_finish({ + 'user': next(iter(self._available_users.keys())) + }) + return self.async_show_form( step_id='init', data_schema=vol.Schema({'user': vol.In(self._available_users)}), diff --git a/homeassistant/components/auth/login_flow.py b/homeassistant/components/auth/login_flow.py index 3a51cf8066f783..c2f03341d2029c 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -81,7 +81,8 @@ async def async_setup(hass, store_result): """Component to allow users to login.""" hass.http.register_view(AuthProvidersView) - hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow)) + hass.http.register_view( + LoginFlowIndexView(hass.auth.login_flow, store_result)) hass.http.register_view( LoginFlowResourceView(hass.auth.login_flow, store_result)) @@ -142,9 +143,10 @@ class LoginFlowIndexView(HomeAssistantView): name = 'api:auth:login_flow' requires_auth = False - def __init__(self, flow_mgr): + def __init__(self, flow_mgr, store_result): """Initialize the flow manager index view.""" self._flow_mgr = flow_mgr + self._store_result = store_result async def get(self, request): """Do not allow index of flows in progress.""" @@ -179,6 +181,12 @@ async def post(self, request, data): except data_entry_flow.UnknownStep: return self.json_message('Handler does not support init', 400) + if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + result.pop('data') + result['result'] = self._store_result( + data['client_id'], result['result']) + return self.json(result) + return self.json(_prepare_result_json(result)) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 4bba80aa154360..6513f9368b0801 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -8,6 +8,7 @@ from socket import _GLOBAL_DEFAULT_TIMEOUT from typing import Any, Union, TypeVar, Callable, Sequence, Dict, Optional from urllib.parse import urlparse +from uuid import UUID import voluptuous as vol from pkg_resources import parse_version @@ -532,6 +533,20 @@ def x10_address(value): return str(value).lower() +def uuid4_hex(value): + """Validate a v4 UUID in hex format.""" + try: + result = UUID(value, version=4) + except (ValueError, AttributeError, TypeError) as error: + raise vol.Invalid('Invalid Version4 UUID', error_message=str(error)) + + if result.hex != value.lower(): + # UUID() will create a uuid4 if input is invalid + raise vol.Invalid('Invalid Version4 UUID') + + return result.hex + + def ensure_list_csv(value: Any) -> Sequence: """Ensure that input is a list or make one from comma-separated string.""" if isinstance(value, str): diff --git a/tests/auth/providers/test_trusted_networks.py b/tests/auth/providers/test_trusted_networks.py index 57e74e750d5623..9468799095c29a 100644 --- a/tests/auth/providers/test_trusted_networks.py +++ b/tests/auth/providers/test_trusted_networks.py @@ -1,5 +1,5 @@ """Test the Trusted Networks auth provider.""" -from ipaddress import ip_address +from ipaddress import ip_address, ip_network import pytest import voluptuous as vol @@ -25,8 +25,47 @@ def provider(hass, store): '192.168.0.1', '192.168.128.0/24', '::1', - 'fd00::/8' - ] + 'fd00::/8', + ], + }) + ) + + +@pytest.fixture +def provider_with_user(hass, store): + """Mock provider with trusted users config.""" + return tn_auth.TrustedNetworksAuthProvider( + hass, store, tn_auth.CONFIG_SCHEMA({ + 'type': 'trusted_networks', + 'trusted_networks': [ + '192.168.0.1', + '192.168.128.0/24', + '::1', + 'fd00::/8', + ], + # user_id will be injected in test + 'trusted_users': { + '192.168.0.1': [], + '192.168.128.0/24': [], + 'fd00::/8': [], + }, + }) + ) + + +@pytest.fixture +def provider_bypass_login(hass, store): + """Mock provider with allow_bypass_login config.""" + return tn_auth.TrustedNetworksAuthProvider( + hass, store, tn_auth.CONFIG_SCHEMA({ + 'type': 'trusted_networks', + 'trusted_networks': [ + '192.168.0.1', + '192.168.128.0/24', + '::1', + 'fd00::/8', + ], + 'allow_bypass_login': True, }) ) @@ -39,6 +78,23 @@ def manager(hass, store, provider): }, {}) +@pytest.fixture +def manager_with_user(hass, store, provider_with_user): + """Mock manager with trusted user.""" + return auth.AuthManager(hass, store, { + (provider_with_user.type, provider_with_user.id): provider_with_user + }, {}) + + +@pytest.fixture +def manager_bypass_login(hass, store, provider_bypass_login): + """Mock manager with allow bypass login.""" + return auth.AuthManager(hass, store, { + (provider_bypass_login.type, provider_bypass_login.id): + provider_bypass_login + }, {}) + + async def test_trusted_networks_credentials(manager, provider): """Test trusted_networks credentials related functions.""" owner = await manager.async_create_user("test-owner") @@ -104,3 +160,157 @@ async def test_login_flow(manager, provider): step = await flow.async_step_init({'user': user.id}) assert step['type'] == 'create_entry' assert step['data']['user'] == user.id + + +async def test_trusted_users_login(manager_with_user, provider_with_user): + """Test available user list changed per different IP.""" + owner = await manager_with_user.async_create_user("test-owner") + sys_user = await manager_with_user.async_create_system_user( + "test-sys-user") # system user will not be available to select + user = await manager_with_user.async_create_user("test-user") + + # change the trusted users config + config = provider_with_user.config['trusted_users'] + assert ip_network('192.168.0.1') in config + config[ip_network('192.168.0.1')] = [owner.id] + assert ip_network('192.168.128.0/24') in config + config[ip_network('192.168.128.0/24')] = [sys_user.id, user.id] + + # not from trusted network + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('127.0.0.1')}) + step = await flow.async_step_init() + assert step['type'] == 'abort' + assert step['reason'] == 'not_whitelisted' + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('192.168.0.1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # only owner listed + assert schema({'user': owner.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': user.id}) + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('192.168.128.1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # only user listed + assert schema({'user': user.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': owner.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': sys_user.id}) + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('::1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # both owner and user listed + assert schema({'user': owner.id}) + assert schema({'user': user.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': sys_user.id}) + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('fd00::1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # no user listed + with pytest.raises(vol.Invalid): + assert schema({'user': owner.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': user.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': sys_user.id}) + + +async def test_trusted_group_login(manager_with_user, provider_with_user): + """Test config trusted_user with group_id.""" + owner = await manager_with_user.async_create_user("test-owner") + # create a user in user group + user = await manager_with_user.async_create_user("test-user") + await manager_with_user.async_update_user( + user, group_ids=[auth.const.GROUP_ID_USER]) + + # change the trusted users config + config = provider_with_user.config['trusted_users'] + assert ip_network('192.168.0.1') in config + config[ip_network('192.168.0.1')] = [{'group': [auth.const.GROUP_ID_USER]}] + assert ip_network('192.168.128.0/24') in config + config[ip_network('192.168.128.0/24')] = [ + owner.id, {'group': [auth.const.GROUP_ID_USER]}] + + # not from trusted network + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('127.0.0.1')}) + step = await flow.async_step_init() + assert step['type'] == 'abort' + assert step['reason'] == 'not_whitelisted' + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('192.168.0.1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # only user listed + print(user.id) + assert schema({'user': user.id}) + with pytest.raises(vol.Invalid): + assert schema({'user': owner.id}) + + # from trusted network, list users intersect trusted_users + flow = await provider_with_user.async_login_flow( + {'ip_address': ip_address('192.168.128.1')}) + step = await flow.async_step_init() + assert step['step_id'] == 'init' + + schema = step['data_schema'] + # both owner and user listed + assert schema({'user': owner.id}) + assert schema({'user': user.id}) + + +async def test_bypass_login_flow(manager_bypass_login, provider_bypass_login): + """Test login flow can be bypass if only one user available.""" + owner = await manager_bypass_login.async_create_user("test-owner") + + # not from trusted network + flow = await provider_bypass_login.async_login_flow( + {'ip_address': ip_address('127.0.0.1')}) + step = await flow.async_step_init() + assert step['type'] == 'abort' + assert step['reason'] == 'not_whitelisted' + + # from trusted network, only one available user, bypass the login flow + flow = await provider_bypass_login.async_login_flow( + {'ip_address': ip_address('192.168.0.1')}) + step = await flow.async_step_init() + assert step['type'] == 'create_entry' + assert step['data']['user'] == owner.id + + user = await manager_bypass_login.async_create_user("test-user") + + # from trusted network, two available user, show up login form + flow = await provider_bypass_login.async_login_flow( + {'ip_address': ip_address('192.168.0.1')}) + step = await flow.async_step_init() + schema = step['data_schema'] + # both owner and user listed + assert schema({'user': owner.id}) + assert schema({'user': user.id}) diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index d83d32c88e37e0..4a883fbf2fd5ec 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -4,6 +4,7 @@ import os from socket import _GLOBAL_DEFAULT_TIMEOUT from unittest.mock import Mock, patch +import uuid import homeassistant import pytest @@ -963,3 +964,24 @@ def test_entity_id_allow_old_validation(caplog): assert "Found invalid entity_id {}".format(value) in caplog.text assert len(cv.INVALID_ENTITY_IDS_FOUND) == 2 + + +def test_uuid4_hex(caplog): + """Test uuid validation.""" + schema = vol.Schema(cv.uuid4_hex) + + for value in ['Not a hex string', '0', 0]: + with pytest.raises(vol.Invalid): + schema(value) + + with pytest.raises(vol.Invalid): + # the 13th char should be 4 + schema('a03d31b22eee1acc9b90eec40be6ed23') + + with pytest.raises(vol.Invalid): + # the 17th char should be 8-a + schema('a03d31b22eee4acc7b90eec40be6ed23') + + hex = uuid.uuid4().hex + assert schema(hex) == hex + assert schema(hex.upper()) == hex From a2c9834852a95aaeed4716c2435ea04090d9a639 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 22:09:25 -0700 Subject: [PATCH 223/605] Bumped version to 0.91.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2d2f00f1e16072..5194c221bed6a1 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0b0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 5d8d9058224ae12fe4e9f6f23226a1f0baa2770c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 22:10:35 -0700 Subject: [PATCH 224/605] Version bump to 0.92.0dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2d2f00f1e16072..9987e0f8e99604 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 -MINOR_VERSION = 91 +MINOR_VERSION = 92 PATCH_VERSION = '0.dev0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) From 4db224ceb5f32bd52e244afbc43900696f319d10 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 23:49:10 -0700 Subject: [PATCH 225/605] Fix YAML --- homeassistant/components/camera/services.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/camera/services.yaml b/homeassistant/components/camera/services.yaml index 45a0f4cfec0963..a3e42300cbd9c1 100644 --- a/homeassistant/components/camera/services.yaml +++ b/homeassistant/components/camera/services.yaml @@ -61,7 +61,8 @@ record: description: Template of a Filename. Variable is entity_id. Must be mp4. example: '/tmp/snapshot_{{ entity_id }}.mp4' duration: - description: (Optional) Target recording length (in seconds). Default: 30 + description: (Optional) Target recording length (in seconds). + default: 30 example: 30 lookback: description: (Optional) Target lookback period (in seconds) to include in addition to duration. Only available if there is currently an active HLS stream. From 3a406f5677c2e88e899193e860733a6195dfda2a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 23:49:10 -0700 Subject: [PATCH 226/605] Fix YAML --- homeassistant/components/camera/services.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/camera/services.yaml b/homeassistant/components/camera/services.yaml index 45a0f4cfec0963..a3e42300cbd9c1 100644 --- a/homeassistant/components/camera/services.yaml +++ b/homeassistant/components/camera/services.yaml @@ -61,7 +61,8 @@ record: description: Template of a Filename. Variable is entity_id. Must be mp4. example: '/tmp/snapshot_{{ entity_id }}.mp4' duration: - description: (Optional) Target recording length (in seconds). Default: 30 + description: (Optional) Target recording length (in seconds). + default: 30 example: 30 lookback: description: (Optional) Target lookback period (in seconds) to include in addition to duration. Only available if there is currently an active HLS stream. From 615b1cbfc7422bab3464fd42e4597d9dce9e36ad Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Mar 2019 23:50:58 -0700 Subject: [PATCH 227/605] Bumped version to 0.91.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 5194c221bed6a1..eacd1812485438 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b0' +PATCH_VERSION = '0b1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 8d86722c0e5dc5ce934e2cbeda529769dcfd28de Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 03:09:12 -0700 Subject: [PATCH 228/605] Fix dev branch (#22493) --- .../components/homekit_controller/__init__.py | 3 +-- homeassistant/loader.py | 14 +++++++++++++- tests/helpers/test_entity_component.py | 2 +- tests/test_config_entries.py | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index f9fd0409c9caaf..44af8bffe26a74 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -1,6 +1,5 @@ """Support for Homekit device discovery.""" import logging -import os from homeassistant.components.discovery import SERVICE_HOMEKIT from homeassistant.helpers import discovery @@ -9,7 +8,7 @@ from .config_flow import load_old_pairings from .connection import get_accessory_information, HKDevice from .const import ( - CONTROLLER, HOMEKIT_DIR, KNOWN_DEVICES, PAIRING_FILE + CONTROLLER, KNOWN_DEVICES ) from .const import DOMAIN # noqa: pylint: disable=unused-import diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 7f0d50f93d4973..8ccbcaa33c4475 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -83,7 +83,11 @@ def get_platform(hass, # type: HomeAssistant """ # If the platform has a component, we will limit the platform loading path # to be the same source (custom/built-in). - component = _load_file(hass, platform_name, LOOKUP_PATHS) + if domain not in ['automation', 'mqtt', 'telegram_bot']: + component = _load_file(hass, platform_name, LOOKUP_PATHS) + else: + # avoid load component for legacy platform + component = None # Until we have moved all platforms under their component/own folder, it # can be that the component is None. @@ -99,6 +103,14 @@ def get_platform(hass, # type: HomeAssistant if platform is not None: return platform + # Legacy platform check for automation: components/automation/event.py + if component is None and domain in ['automation', 'mqtt', 'telegram_bot']: + platform = _load_file( + hass, + PLATFORM_FORMAT.format(domain=platform_name, platform=domain), + base_paths + ) + # Legacy platform check for custom: custom_components/light/hue.py # Only check if the component was also in custom components. if component is None or base_paths[0] == PACKAGE_CUSTOM_COMPONENTS: diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index 163261a4b81158..6da3293d597b5c 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -324,7 +324,7 @@ def test_setup_dependencies_platform(hass): loader.set_component(hass, 'test_component2', MockModule('test_component2')) loader.set_component( - hass, 'test_domain.test_component', + hass, 'test_component.test_domain', MockPlatform(dependencies=['test_component', 'test_component2'])) component = EntityComponent(_LOGGER, DOMAIN, hass) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 324db971583548..32532761ccf9f0 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -192,7 +192,7 @@ async def mock_setup_entry_platform(hass, entry, async_add_entities): async_remove_entry=mock_remove_entry )) loader.set_component( - hass, 'light.test', + hass, 'test.light', MockPlatform(async_setup_entry=mock_setup_entry_platform)) MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager) From 59476ab475122bb02ddbdd2abcd4930cdfece849 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 09:54:49 -0700 Subject: [PATCH 229/605] A very basic Circleci setup (#22503) * Add circleci support * Add buildpack-deps * Install libudev-dev * sudo * always run test * Add test report * no sugar * quite pytest * better junit test result * Add $CODE_COVERAGE env var --- .circleci/config.yml | 79 ++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 80 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000000..b6a57a283815cb --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,79 @@ +# Python CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-python/ for more details +# +version: 2.1 +jobs: + build: + docker: + # specify the version you desire here + # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` + - image: circleci/python:3.7.2 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/postgres:9.4 + - image: circleci/buildpack-deps:stretch + + working_directory: ~/repo + + steps: + - checkout + + - run: + name: setup docker prereqs + command: sudo apt-get update && sudo apt-get install -y --no-install-recommends libudev-dev + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "requirements_all.txt" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: + name: install dependencies + command: | + python3 -m venv venv + . venv/bin/activate + pip install --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt + + - save_cache: + paths: + - ./venv + key: v1-dependencies-{{ checksum "requirements_all.txt" }} + + - run: + name: install + command: | + . venv/bin/activate + pip install --progress-bar off -e . + + - run: + name: run lint + command: | + . venv/bin/activate + python script/gen_requirements_all.py validate + flake8 + pylint homeassistant + + - run: + name: run tests + command: | + . venv/bin/activate + if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi + pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH + script/check_dirty + when: always + + - store_test_results: + path: test-reports + + - store_artifacts: + path: htmlcov + destination: cov-reports + + - store_artifacts: + path: test-reports + destination: test-reports \ No newline at end of file diff --git a/.gitignore b/.gitignore index 91b8d024aedd02..b486032c741429 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ pip-log.txt .tox nosetests.xml htmlcov/ +test-reports/ # Translations *.mo From 8874422e8a783563edc16a50790c5a033ac39f18 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 14:37:10 -0700 Subject: [PATCH 230/605] Fix Circleci config (#22509) * Add libav depends on circleci * tweak circleci config --- .circleci/config.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b6a57a283815cb..112ce2284dde58 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,26 +23,26 @@ jobs: - run: name: setup docker prereqs - command: sudo apt-get update && sudo apt-get install -y --no-install-recommends libudev-dev + command: sudo apt-get update && sudo apt-get install -y --no-install-recommends + libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev + libswscale-dev libswresample-dev libavfilter-dev - # Download and cache dependencies + # Download and cache dependencies, we don't use fallback cache - restore_cache: keys: - - v1-dependencies-{{ checksum "requirements_all.txt" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- + - v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - run: name: install dependencies command: | python3 -m venv venv . venv/bin/activate - pip install --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt + pip install -q --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt - save_cache: paths: - ./venv - key: v1-dependencies-{{ checksum "requirements_all.txt" }} + key: v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - run: name: install @@ -76,4 +76,4 @@ jobs: - store_artifacts: path: test-reports - destination: test-reports \ No newline at end of file + destination: test-reports From 821a90fa5482638fba973f249816e2bf11a94f0f Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 14:37:44 -0700 Subject: [PATCH 231/605] Remove botocore dependency from credstash script (#22511) * Remove botocore dependency from credstash script * Update requirements_all.txt * Update pylintrc * Update credstash.py --- homeassistant/scripts/credstash.py | 7 +++---- pylintrc | 1 - requirements_all.txt | 3 --- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/homeassistant/scripts/credstash.py b/homeassistant/scripts/credstash.py index 302910c5b08109..6dd9f90197a86b 100644 --- a/homeassistant/scripts/credstash.py +++ b/homeassistant/scripts/credstash.py @@ -4,7 +4,7 @@ from homeassistant.util.yaml import _SECRET_NAMESPACE -REQUIREMENTS = ['credstash==1.15.0', 'botocore==1.7.34'] +REQUIREMENTS = ['credstash==1.15.0'] def run(args): @@ -24,16 +24,15 @@ def run(args): 'value', help="The value to save when putting a secret", nargs='?', default=None) - # pylint: disable=import-error, no-member + # pylint: disable=no-member import credstash - import botocore args = parser.parse_args(args) table = _SECRET_NAMESPACE try: credstash.listSecrets(table=table) - except botocore.errorfactory.ClientError: + except Exception: # pylint: disable=broad-except credstash.createDdbTable(table=table) if args.action == 'list': diff --git a/pylintrc b/pylintrc index a88aabe1936f1a..7d349033f70ed9 100644 --- a/pylintrc +++ b/pylintrc @@ -42,7 +42,6 @@ reports=no [TYPECHECK] # For attrs ignored-classes=_CountingAttr -generated-members=botocore.errorfactory [FORMAT] expected-line-ending-format=LF diff --git a/requirements_all.txt b/requirements_all.txt index 4af334dd01ebda..1832aa5cf659ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -235,9 +235,6 @@ blockchain==1.4.4 # homeassistant.components.aws_sqs.notify boto3==1.9.16 -# homeassistant.scripts.credstash -botocore==1.7.34 - # homeassistant.components.braviatv.media_player braviarc-homeassistant==0.3.7.dev0 From ee8cd861e05981a7b9c6be902cab7c444eb01cf4 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Thu, 28 Mar 2019 23:09:45 +0100 Subject: [PATCH 232/605] Add LCN binary_sensor component (#22341) --- homeassistant/components/lcn/__init__.py | 26 +++- homeassistant/components/lcn/binary_sensor.py | 139 ++++++++++++++++++ homeassistant/components/lcn/const.py | 7 + 3 files changed, 164 insertions(+), 8 deletions(-) create mode 100755 homeassistant/components/lcn/binary_sensor.py diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index e380c2bb4a1cfd..44f69c261b9c88 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -4,19 +4,19 @@ import voluptuous as vol from homeassistant.const import ( - CONF_ADDRESS, CONF_COVERS, CONF_HOST, CONF_LIGHTS, CONF_NAME, - CONF_PASSWORD, CONF_PORT, CONF_SENSORS, CONF_SWITCHES, + CONF_ADDRESS, CONF_BINARY_SENSORS, CONF_COVERS, CONF_HOST, CONF_LIGHTS, + CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SENSORS, CONF_SWITCHES, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.entity import Entity from .const import ( - CONF_CONNECTIONS, CONF_DIM_MODE, CONF_DIMMABLE, CONF_MOTOR, CONF_OUTPUT, - CONF_SK_NUM_TRIES, CONF_SOURCE, CONF_TRANSITION, DATA_LCN, DEFAULT_NAME, - DIM_MODES, DOMAIN, LED_PORTS, LOGICOP_PORTS, MOTOR_PORTS, OUTPUT_PORTS, - PATTERN_ADDRESS, RELAY_PORTS, S0_INPUTS, SETPOINTS, THRESHOLDS, VAR_UNITS, - VARIABLES) + BINSENSOR_PORTS, CONF_CONNECTIONS, CONF_DIM_MODE, CONF_DIMMABLE, + CONF_MOTOR, CONF_OUTPUT, CONF_SK_NUM_TRIES, CONF_SOURCE, CONF_TRANSITION, + DATA_LCN, DEFAULT_NAME, DIM_MODES, DOMAIN, KEYS, LED_PORTS, LOGICOP_PORTS, + MOTOR_PORTS, OUTPUT_PORTS, PATTERN_ADDRESS, RELAY_PORTS, S0_INPUTS, + SETPOINTS, THRESHOLDS, VAR_UNITS, VARIABLES) _LOGGER = logging.getLogger(__name__) @@ -65,6 +65,13 @@ def is_address(value): raise vol.error.Invalid('Not a valid address string.') +BINARY_SENSORS_SCHEMA = vol.Schema({ + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_ADDRESS): is_address, + vol.Required(CONF_SOURCE): vol.All(vol.Upper, vol.In(SETPOINTS + KEYS + + BINSENSOR_PORTS)) + }) + COVERS_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_ADDRESS): is_address, @@ -115,6 +122,8 @@ def is_address(value): DOMAIN: vol.Schema({ vol.Required(CONF_CONNECTIONS): vol.All( cv.ensure_list, has_unique_connection_names, [CONNECTION_SCHEMA]), + vol.Optional(CONF_BINARY_SENSORS): vol.All( + cv.ensure_list, [BINARY_SENSORS_SCHEMA]), vol.Optional(CONF_COVERS): vol.All( cv.ensure_list, [COVERS_SCHEMA]), vol.Optional(CONF_LIGHTS): vol.All( @@ -177,7 +186,8 @@ async def async_setup(hass, config): hass.data[DATA_LCN][CONF_CONNECTIONS] = connections # load platforms - for component, conf_key in (('cover', CONF_COVERS), + for component, conf_key in (('binary_sensor', CONF_BINARY_SENSORS), + ('cover', CONF_COVERS), ('light', CONF_LIGHTS), ('sensor', CONF_SENSORS), ('switch', CONF_SWITCHES)): diff --git a/homeassistant/components/lcn/binary_sensor.py b/homeassistant/components/lcn/binary_sensor.py new file mode 100755 index 00000000000000..0ffa2e50d8b21d --- /dev/null +++ b/homeassistant/components/lcn/binary_sensor.py @@ -0,0 +1,139 @@ +"""Support for LCN binary sensors.""" +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.const import CONF_ADDRESS + +from . import LcnDevice, get_connection +from .const import ( + BINSENSOR_PORTS, CONF_CONNECTIONS, CONF_SOURCE, DATA_LCN, SETPOINTS) + +DEPENDENCIES = ['lcn'] + + +async def async_setup_platform(hass, hass_config, async_add_entities, + discovery_info=None): + """Set up the LCN binary sensor platform.""" + if discovery_info is None: + return + + import pypck + + devices = [] + for config in discovery_info: + address, connection_id = config[CONF_ADDRESS] + addr = pypck.lcn_addr.LcnAddr(*address) + connections = hass.data[DATA_LCN][CONF_CONNECTIONS] + connection = get_connection(connections, connection_id) + address_connection = connection.get_address_conn(addr) + + if config[CONF_SOURCE] in SETPOINTS: + device = LcnRegulatorLockSensor(config, address_connection) + elif config[CONF_SOURCE] in BINSENSOR_PORTS: + device = LcnBinarySensor(config, address_connection) + else: # in KEYS + device = LcnLockKeysSensor(config, address_connection) + + devices.append(device) + + async_add_entities(devices) + + +class LcnRegulatorLockSensor(LcnDevice, BinarySensorDevice): + """Representation of a LCN binary sensor for regulator locks.""" + + def __init__(self, config, address_connection): + """Initialize the LCN binary sensor.""" + super().__init__(config, address_connection) + + self.setpoint_variable = \ + self.pypck.lcn_defs.Var[config[CONF_SOURCE]] + + self._value = None + + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + self.hass.async_create_task( + self.address_connection.activate_status_request_handler( + self.setpoint_variable)) + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self._value + + def input_received(self, input_obj): + """Set sensor value when LCN input object (command) is received.""" + if not isinstance(input_obj, self.pypck.inputs.ModStatusVar) or \ + input_obj.get_var() != self.setpoint_variable: + return + + self._value = input_obj.get_value().is_locked_regulator() + self.async_schedule_update_ha_state() + + +class LcnBinarySensor(LcnDevice, BinarySensorDevice): + """Representation of a LCN binary sensor for binary sensor ports.""" + + def __init__(self, config, address_connection): + """Initialize the LCN binary sensor.""" + super().__init__(config, address_connection) + + self.bin_sensor_port = \ + self.pypck.lcn_defs.BinSensorPort[config[CONF_SOURCE]] + + self._value = None + + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + self.hass.async_create_task( + self.address_connection.activate_status_request_handler( + self.bin_sensor_port)) + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self._value + + def input_received(self, input_obj): + """Set sensor value when LCN input object (command) is received.""" + if not isinstance(input_obj, self.pypck.inputs.ModStatusBinSensors): + return + + self._value = input_obj.get_state(self.bin_sensor_port.value) + self.async_schedule_update_ha_state() + + +class LcnLockKeysSensor(LcnDevice, BinarySensorDevice): + """Representation of a LCN sensor for key locks.""" + + def __init__(self, config, address_connection): + """Initialize the LCN sensor.""" + super().__init__(config, address_connection) + + self.source = self.pypck.lcn_defs.Key[config[CONF_SOURCE]] + self._value = None + + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + self.hass.async_create_task( + self.address_connection.activate_status_request_handler( + self.source)) + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self._value + + def input_received(self, input_obj): + """Set sensor value when LCN input object (command) is received.""" + if not isinstance(input_obj, self.pypck.inputs.ModStatusKeyLocks) or \ + self.source not in self.pypck.lcn_defs.Key: + return + + table_id = ord(self.source.name[0]) - 65 + key_id = int(self.source.name[1]) - 1 + + self._value = input_obj.get_state(table_id, key_id) + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/lcn/const.py b/homeassistant/components/lcn/const.py index ee7a3a79cde48a..b745d0636c2f38 100644 --- a/homeassistant/components/lcn/const.py +++ b/homeassistant/components/lcn/const.py @@ -1,5 +1,6 @@ # coding: utf-8 """Constants for the LCN component.""" +from itertools import product import re from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT @@ -37,6 +38,12 @@ LOGICOP_PORTS = ['LOGICOP1', 'LOGICOP2', 'LOGICOP3', 'LOGICOP4'] +BINSENSOR_PORTS = ['BINSENSOR1', 'BINSENSOR2', 'BINSENSOR3', 'BINSENSOR4', + 'BINSENSOR5', 'BINSENSOR6', 'BINSENSOR7', 'BINSENSOR8'] + +KEYS = ['{:s}{:d}'.format(t[0], t[1]) for t in product(['A', 'B', 'C', 'D'], + range(1, 9))] + VARIABLES = ['VAR1ORTVAR', 'VAR2ORR1VAR', 'VAR3ORR2VAR', 'TVAR', 'R1VAR', 'R2VAR', 'VAR1', 'VAR2', 'VAR3', 'VAR4', 'VAR5', 'VAR6', From 709419e465a1472b7d453b21068a1e9189f4d377 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 15:33:21 -0700 Subject: [PATCH 233/605] Fix lint on dev (#22512) ## Description: Fix a lint issue in credstash script. **Related issue (if applicable):** fixes # **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io# ## Example entry for `configuration.yaml` (if applicable): ```yaml ``` ## Checklist: - [ ] The code change is tested and works locally. - [ ] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [ ] New files were added to `.coveragerc`. If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- homeassistant/scripts/credstash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/scripts/credstash.py b/homeassistant/scripts/credstash.py index 6dd9f90197a86b..e2950f8d7a0675 100644 --- a/homeassistant/scripts/credstash.py +++ b/homeassistant/scripts/credstash.py @@ -24,7 +24,7 @@ def run(args): 'value', help="The value to save when putting a secret", nargs='?', default=None) - # pylint: disable=no-member + # pylint: disable=import-error, no-member import credstash args = parser.parse_args(args) From 01052f516b5d8a014bb9eed071cb556f74dca6fd Mon Sep 17 00:00:00 2001 From: Andreas Rydbrink Date: Fri, 29 Mar 2019 03:03:02 +0100 Subject: [PATCH 234/605] Add HEOS media player component (#21721) ## Description: Denon HEOS media player. **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io#8848 ## Example entry for `configuration.yaml` (if applicable): ```yaml heos: host: HEOS-1 ``` ## Checklist: - [X] The code change is tested and works locally. - [X] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [X] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [X] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [X] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [X] New dependencies are only imported inside functions that use them ([example][ex-import]). - [X] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [X] New files were added to `.coveragerc`. If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 Co-authored-by: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> --- .coveragerc | 4 + homeassistant/components/heos/__init__.py | 52 ++++++ homeassistant/components/heos/media_player.py | 152 ++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 211 insertions(+) create mode 100644 homeassistant/components/heos/__init__.py create mode 100644 homeassistant/components/heos/media_player.py diff --git a/.coveragerc b/.coveragerc index 3cba85193140e5..662d880af1b701 100644 --- a/.coveragerc +++ b/.coveragerc @@ -235,11 +235,15 @@ omit = homeassistant/components/harmony/remote.py homeassistant/components/haveibeenpwned/sensor.py homeassistant/components/hdmi_cec/* +<<<<<<< HEAD homeassistant/components/heatmiser/climate.py homeassistant/components/hikvision/binary_sensor.py homeassistant/components/hikvisioncam/switch.py homeassistant/components/hipchat/notify.py homeassistant/components/hitron_coda/device_tracker.py +======= + homeassistant/components/heos/* +>>>>>>> Update HEOS to support multiple speaker and conformance. homeassistant/components/hive/* homeassistant/components/hlk_sw16/* homeassistant/components/homekit_controller/* diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py new file mode 100644 index 00000000000000..e9b775b05d0834 --- /dev/null +++ b/homeassistant/components/heos/__init__.py @@ -0,0 +1,52 @@ +"""Denon HEOS Media Player.""" + +import asyncio +import logging + +import voluptuous as vol + +from homeassistant.components.media_player.const import ( + DOMAIN as MEDIA_PLAYER_DOMAIN) +from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.discovery import async_load_platform +from homeassistant.helpers.typing import ConfigType, HomeAssistantType + +DOMAIN = 'heos' +REQUIREMENTS = ['aioheos==0.4.0'] + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_HOST): cv.string + }) +}, extra=vol.ALLOW_EXTRA) + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass: HomeAssistantType, config: ConfigType): + """Set up the HEOS component.""" + from aioheos import AioHeosController + + host = config[DOMAIN][CONF_HOST] + controller = AioHeosController(hass.loop, host) + + try: + await asyncio.wait_for(controller.connect(), timeout=5.0) + except asyncio.TimeoutError: + _LOGGER.error('Timeout during setup.') + return False + + async def controller_close(event): + """Close connection when HASS shutsdown.""" + await controller.close() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller_close) + + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] = controller + + hass.async_create_task(async_load_platform( + hass, MEDIA_PLAYER_DOMAIN, DOMAIN, {}, config)) + + return True diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py new file mode 100644 index 00000000000000..8047ffd0775c6e --- /dev/null +++ b/homeassistant/components/heos/media_player.py @@ -0,0 +1,152 @@ +"""Denon HEOS Media Player.""" + +from homeassistant.components.media_player import MediaPlayerDevice +from homeassistant.components.media_player.const import ( + DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, + SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_STOP, + SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) +from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING + +from . import DOMAIN as HEOS_DOMAIN + +DEPENDENCIES = ["heos"] + +SUPPORT_HEOS = ( + SUPPORT_PLAY + | SUPPORT_STOP + | SUPPORT_PAUSE + | SUPPORT_PLAY_MEDIA + | SUPPORT_PREVIOUS_TRACK + | SUPPORT_NEXT_TRACK + | SUPPORT_VOLUME_MUTE + | SUPPORT_VOLUME_SET + | SUPPORT_VOLUME_STEP +) + +PLAY_STATE_TO_STATE = { + "play": STATE_PLAYING, + "pause": STATE_PAUSED, + "stop": STATE_IDLE, +} + + +async def async_setup_platform(hass, config, async_add_devices, + discover_info=None): + """Set up the HEOS platform.""" + controller = hass.data[HEOS_DOMAIN][DOMAIN] + players = controller.get_players() + devices = [HeosMediaPlayer(p) for p in players] + async_add_devices(devices, True) + + +class HeosMediaPlayer(MediaPlayerDevice): + """The HEOS player.""" + + def __init__(self, player): + """Initialize.""" + self._player = player + + def _update_state(self): + self.async_schedule_update_ha_state() + + async def async_update(self): + """Update the player.""" + self._player.request_update() + + async def async_added_to_hass(self): + """Device added to hass.""" + self._player.state_change_callback = self._update_state + + @property + def unique_id(self): + """Get unique id of the player.""" + return self._player.player_id + + @property + def name(self): + """Return the name of the device.""" + return self._player.name + + @property + def volume_level(self): + """Volume level of the device (0..1).""" + volume = self._player.volume + return float(volume) / 100 + + @property + def state(self): + """Get state.""" + return PLAY_STATE_TO_STATE.get(self._player.play_state) + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def media_content_type(self): + """Content type of current playing media.""" + return MEDIA_TYPE_MUSIC + + @property + def media_artist(self): + """Artist of current playing media.""" + return self._player.media_artist + + @property + def media_title(self): + """Album name of current playing media.""" + return self._player.media_title + + @property + def media_album_name(self): + """Album name of current playing media.""" + return self._player.media_album + + @property + def media_image_url(self): + """Return the image url of current playing media.""" + return self._player.media_image_url + + @property + def media_content_id(self): + """Return the content ID of current playing media.""" + return self._player.media_id + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return self._player.mute == "on" + + async def async_mute_volume(self, mute): + """Mute volume.""" + self._player.set_mute(mute) + + async def async_media_next_track(self): + """Go TO next track.""" + self._player.play_next() + + async def async_media_previous_track(self): + """Go TO previous track.""" + self._player.play_previous() + + @property + def supported_features(self): + """Flag of media commands that are supported.""" + return SUPPORT_HEOS + + async def async_set_volume_level(self, volume): + """Set volume level, range 0..1.""" + self._player.set_volume(volume * 100) + + async def async_media_play(self): + """Play media player.""" + self._player.play() + + async def async_media_stop(self): + """Stop media player.""" + self._player.stop() + + async def async_media_pause(self): + """Pause media player.""" + self._player.pause() diff --git a/requirements_all.txt b/requirements_all.txt index 1832aa5cf659ab..31b62eb824b5be 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -123,6 +123,9 @@ aioftp==0.12.0 # homeassistant.components.harmony.remote aioharmony==0.1.8 +# homeassistant.components.heos +aioheos==0.4.0 + # homeassistant.components.emulated_hue # homeassistant.components.http aiohttp_cors==0.7.0 From e14dbfb006a96cdac47495a7c866a1fbb2d014ee Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Fri, 29 Mar 2019 05:56:12 +0300 Subject: [PATCH 235/605] Add google calendar max_results config option (#21874) * Added max_results config capability to google calendar (people are creating custom components just to override that) * Dummy commit * Dummy commit 2 * Changed to positive_int * Removed double imports --- homeassistant/components/google/__init__.py | 2 ++ homeassistant/components/google/calendar.py | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 8fba016df57a4a..37ee5efbd93b81 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -36,6 +36,7 @@ CONF_SEARCH = 'search' CONF_OFFSET = 'offset' CONF_IGNORE_AVAILABILITY = 'ignore_availability' +CONF_MAX_RESULTS = 'max_results' DEFAULT_CONF_TRACK_NEW = True DEFAULT_CONF_OFFSET = '!!' @@ -69,6 +70,7 @@ vol.Optional(CONF_OFFSET): cv.string, vol.Optional(CONF_SEARCH): cv.string, vol.Optional(CONF_TRACK): cv.boolean, + vol.Optional(CONF_MAX_RESULTS): cv.positive_int, }) DEVICE_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 9f71e7c4f20367..36ab3459d5cbd8 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -7,7 +7,7 @@ from . import ( CONF_CAL_ID, CONF_ENTITIES, CONF_IGNORE_AVAILABILITY, CONF_SEARCH, - CONF_TRACK, TOKEN_FILE, GoogleCalendarService) + CONF_TRACK, TOKEN_FILE, CONF_MAX_RESULTS, GoogleCalendarService) _LOGGER = logging.getLogger(__name__) @@ -41,7 +41,8 @@ def __init__(self, hass, calendar_service, calendar, data): """Create the Calendar event device.""" self.data = GoogleCalendarData(calendar_service, calendar, data.get(CONF_SEARCH), - data.get(CONF_IGNORE_AVAILABILITY)) + data.get(CONF_IGNORE_AVAILABILITY), + data.get(CONF_MAX_RESULTS)) super().__init__(hass, data) @@ -54,12 +55,13 @@ class GoogleCalendarData: """Class to utilize calendar service object to get next event.""" def __init__(self, calendar_service, calendar_id, search, - ignore_availability): + ignore_availability, max_results): """Set up how we are going to search the google calendar.""" self.calendar_service = calendar_service self.calendar_id = calendar_id self.search = search self.ignore_availability = ignore_availability + self.max_results = max_results self.event = None def _prepare_query(self): @@ -73,6 +75,8 @@ def _prepare_query(self): return False params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS) params['calendarId'] = self.calendar_id + if self.max_results: + params['max_results'] = self.max_results if self.search: params['q'] = self.search From 78047c8c3cae5cf2aec58845367af6c9eec1bac9 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Thu, 28 Mar 2019 22:01:53 -0500 Subject: [PATCH 236/605] Fix .coveragerc from merge/rebase (#22516) * Fix coveragerc * Fix coveragerc --- .coveragerc | 3 --- 1 file changed, 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 662d880af1b701..1cf32519adb23d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -235,15 +235,12 @@ omit = homeassistant/components/harmony/remote.py homeassistant/components/haveibeenpwned/sensor.py homeassistant/components/hdmi_cec/* -<<<<<<< HEAD homeassistant/components/heatmiser/climate.py homeassistant/components/hikvision/binary_sensor.py homeassistant/components/hikvisioncam/switch.py homeassistant/components/hipchat/notify.py homeassistant/components/hitron_coda/device_tracker.py -======= homeassistant/components/heos/* ->>>>>>> Update HEOS to support multiple speaker and conformance. homeassistant/components/hive/* homeassistant/components/hlk_sw16/* homeassistant/components/homekit_controller/* From 424543f34afe94b7d2dca26a4bfe0cb9ac825bac Mon Sep 17 00:00:00 2001 From: mvn23 Date: Fri, 29 Mar 2019 08:28:50 +0100 Subject: [PATCH 237/605] Update pyotgw to 0.4b3 (#22496) --- homeassistant/components/opentherm_gw/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index acb277c0ef5d0e..1476363c6bd877 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyotgw==0.4b2'] +REQUIREMENTS = ['pyotgw==0.4b3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 31b62eb824b5be..281b0434148b99 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1198,7 +1198,7 @@ pyoppleio==1.0.5 pyota==2.0.5 # homeassistant.components.opentherm_gw -pyotgw==0.4b2 +pyotgw==0.4b3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp From f4625fd561d6c53be4e8f4ebf330f15ad5d62733 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 29 Mar 2019 02:38:58 -0600 Subject: [PATCH 238/605] Speed up status updating in SimpliSafe (#22506) * Speed up status updating in SimpliSafe * Linting * Member comments --- .../components/simplisafe/__init__.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index e6b9aba643da06..359591856a7917 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -1,4 +1,5 @@ """Support for SimpliSafe alarm systems.""" +import asyncio import logging from datetime import timedelta @@ -107,17 +108,18 @@ async def async_setup_entry(hass, config_entry): async def refresh(event_time): """Refresh data from the SimpliSafe account.""" - for system in systems: - _LOGGER.debug('Updating system data: %s', system.system_id) - - try: - await system.update() - except SimplipyError as err: + tasks = [system.update() for system in systems] + results = await asyncio.gather(*tasks, return_exceptions=True) + for system, result in zip(systems, results): + if isinstance(result, SimplipyError): _LOGGER.error( - 'There was error updating "%s": %s', system.address, err) + 'There was error updating "%s": %s', system.address, + result) continue - async_dispatcher_send(hass, TOPIC_UPDATE.format(system.system_id)) + _LOGGER.debug('Updated status of "%s"', system.address) + async_dispatcher_send( + hass, TOPIC_UPDATE.format(system.system_id)) if system.api.refresh_token_dirty: _async_save_refresh_token( From 5f6037d56399c060e7d5fff3aa485fc35a97d304 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 29 Mar 2019 15:20:12 +0100 Subject: [PATCH 239/605] Axis component reflect device availability (#22401) --- homeassistant/components/axis/__init__.py | 2 +- .../components/axis/binary_sensor.py | 20 +++++++++--- homeassistant/components/axis/camera.py | 20 ++++++++++-- homeassistant/components/axis/device.py | 32 +++++++++++++++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/axis/test_binary_sensor.py | 4 +-- tests/components/axis/test_camera.py | 4 +-- tests/components/axis/test_config_flow.py | 9 ++---- tests/components/axis/test_device.py | 2 +- 10 files changed, 69 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/axis/__init__.py b/homeassistant/components/axis/__init__.py index 53087f2682c862..6082c96863f965 100644 --- a/homeassistant/components/axis/__init__.py +++ b/homeassistant/components/axis/__init__.py @@ -12,7 +12,7 @@ from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN from .device import AxisNetworkDevice, get_device -REQUIREMENTS = ['axis==17'] +REQUIREMENTS = ['axis==19'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: cv.schema_with_slug_keys(DEVICE_SCHEMA), diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index 6d373dd638f67f..30e0e759a2cda3 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -35,23 +35,29 @@ def __init__(self, event, device): """Initialize the Axis binary sensor.""" self.event = event self.device = device - self.delay = device.config_entry.options[CONF_TRIGGER_TIME] self.remove_timer = None + self.unsub_dispatcher = None async def async_added_to_hass(self): """Subscribe sensors events.""" self.event.register_callback(self.update_callback) + self.unsub_dispatcher = async_dispatcher_connect( + self.hass, self.device.event_reachable, self.update_callback) - def update_callback(self): - """Update the sensor's state, if needed.""" + @callback + def update_callback(self, no_delay=False): + """Update the sensor's state, if needed. + + Parameter no_delay is True when device_event_reachable is sent. + """ delay = self.device.config_entry.options[CONF_TRIGGER_TIME] if self.remove_timer is not None: self.remove_timer() self.remove_timer = None - if delay == 0 or self.is_on: - self.schedule_update_ha_state() + if self.is_on or delay == 0 or no_delay: + self.async_schedule_update_ha_state() return @callback @@ -87,6 +93,10 @@ def unique_id(self): return '{}-{}-{}'.format( self.device.serial, self.event.topic, self.event.id) + def available(self): + """Return True if device is available.""" + return self.device.available + @property def should_poll(self): """No polling needed.""" diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index 45801257d00463..34b6da778a8e6e 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -5,6 +5,7 @@ from homeassistant.const import ( CONF_AUTHENTICATION, CONF_DEVICE, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_USERNAME, HTTP_DIGEST_AUTHENTICATION) +from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import DOMAIN as AXIS_DOMAIN @@ -46,12 +47,25 @@ def __init__(self, config, device): self.device_config = config self.device = device self.port = device.config_entry.data[CONF_DEVICE][CONF_PORT] - self.unsub_dispatcher = None + self.unsub_dispatcher = [] async def async_added_to_hass(self): """Subscribe camera events.""" - self.unsub_dispatcher = async_dispatcher_connect( - self.hass, 'axis_{}_new_ip'.format(self.device.name), self._new_ip) + self.unsub_dispatcher.append(async_dispatcher_connect( + self.hass, 'axis_{}_new_ip'.format(self.device.name), + self._new_ip)) + self.unsub_dispatcher.append(async_dispatcher_connect( + self.hass, self.device.event_reachable, self.update_callback)) + + @callback + def update_callback(self, no_delay=None): + """Update the cameras state.""" + self.async_schedule_update_ha_state() + + @property + def available(self): + """Return True if device is available.""" + return self.device.available def _new_ip(self, host): """Set new IP for video stream.""" diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index ffe48e5f733b1c..746808e0d915f1 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -12,6 +12,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from .const import CONF_CAMERA, CONF_EVENTS, CONF_MODEL, DOMAIN, LOGGER + from .errors import AuthenticationRequired, CannotConnect @@ -72,8 +73,7 @@ async def async_setup(self): try: self.api = await get_device( - hass, self.config_entry.data[CONF_DEVICE], - event_types='on', signal_callback=self.async_signal_callback) + hass, self.config_entry.data[CONF_DEVICE]) except CannotConnect: raise ConfigEntryNotReady @@ -95,17 +95,38 @@ async def async_setup(self): self.hass.async_create_task( self.hass.config_entries.async_forward_entry_setup( self.config_entry, 'binary_sensor')) + + self.api.stream.connection_status_callback = \ + self.async_connection_status_callback + self.api.enable_events(event_callback=self.async_event_callback) self.api.start() return True + @property + def event_reachable(self): + """Device specific event to signal a change in connection status.""" + return 'axis_reachable_{}'.format(self.serial) + + @callback + def async_connection_status_callback(self, status): + """Handle signals of gateway connection status. + + This is called on every RTSP keep-alive message. + Only signal state change if state change is true. + """ + from axis.streammanager import SIGNAL_PLAYING + if self.available != (status == SIGNAL_PLAYING): + self.available = not self.available + async_dispatcher_send(self.hass, self.event_reachable, True) + @property def event_new_sensor(self): """Device specific event to signal new sensor available.""" return 'axis_add_sensor_{}'.format(self.serial) @callback - def async_signal_callback(self, action, event): + def async_event_callback(self, action, event): """Call to configure events when initialized on event stream.""" if action == 'add': async_dispatcher_send(self.hass, self.event_new_sensor, event) @@ -116,7 +137,7 @@ def shutdown(self, event): self.api.stop() -async def get_device(hass, config, event_types=None, signal_callback=None): +async def get_device(hass, config): """Create a Axis device.""" import axis @@ -124,8 +145,7 @@ async def get_device(hass, config, event_types=None, signal_callback=None): loop=hass.loop, host=config[CONF_HOST], username=config[CONF_USERNAME], password=config[CONF_PASSWORD], - port=config[CONF_PORT], web_proto='http', - event_types=event_types, signal=signal_callback) + port=config[CONF_PORT], web_proto='http') try: with async_timeout.timeout(15): diff --git a/requirements_all.txt b/requirements_all.txt index 281b0434148b99..66ad3d044303d9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -192,7 +192,7 @@ av==6.1.2 # avion==0.10 # homeassistant.components.axis -axis==17 +axis==19 # homeassistant.components.modem_callerid.sensor basicmodem==0.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 60d9697ed191f7..414f8c459194c9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -60,7 +60,7 @@ apns2==0.3.0 av==6.1.2 # homeassistant.components.axis -axis==17 +axis==19 # homeassistant.components.zha bellows-homeassistant==0.7.1 diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py index 9ca8b81793ba6a..75dd6462c4e9e1 100644 --- a/tests/components/axis/test_binary_sensor.py +++ b/tests/components/axis/test_binary_sensor.py @@ -53,9 +53,9 @@ async def setup_device(hass): 1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test', config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS) device = axis.AxisNetworkDevice(hass, config_entry) - device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE], - signal=device.async_signal_callback) + device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE]) hass.data[axis.DOMAIN] = {device.serial: device} + device.api.enable_events(event_callback=device.async_event_callback) await hass.config_entries.async_forward_entry_setup( config_entry, 'binary_sensor') diff --git a/tests/components/axis/test_camera.py b/tests/components/axis/test_camera.py index c585ada631978f..95878697e03ebb 100644 --- a/tests/components/axis/test_camera.py +++ b/tests/components/axis/test_camera.py @@ -37,9 +37,9 @@ async def setup_device(hass): 1, axis.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test', config_entries.CONN_CLASS_LOCAL_PUSH, options=ENTRY_OPTIONS) device = axis.AxisNetworkDevice(hass, config_entry) - device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE], - signal=device.async_signal_callback) + device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE]) hass.data[axis.DOMAIN] = {device.serial: device} + device.api.enable_events(event_callback=device.async_event_callback) await hass.config_entries.async_forward_entry_setup( config_entry, 'camera') diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py index 7e18b36c6a6c27..086c2692d4487c 100644 --- a/tests/components/axis/test_config_flow.py +++ b/tests/components/axis/test_config_flow.py @@ -31,8 +31,7 @@ async def test_flow_works(hass): with patch('axis.AxisDevice') as mock_device: def mock_constructor( - loop, host, username, password, port, web_proto, event_types, - signal): + loop, host, username, password, port, web_proto): """Fake the controller constructor.""" mock_device.loop = loop mock_device.host = host @@ -189,8 +188,7 @@ async def test_discovery_flow_known_device(hass): config_flow.CONF_PORT: 80}}), \ patch('axis.AxisDevice') as mock_device: def mock_constructor( - loop, host, username, password, port, web_proto, event_types, - signal): + loop, host, username, password, port, web_proto): """Fake the controller constructor.""" mock_device.loop = loop mock_device.host = host @@ -277,8 +275,7 @@ async def test_import_flow_works(hass): with patch('axis.AxisDevice') as mock_device: def mock_constructor( - loop, host, username, password, port, web_proto, event_types, - signal): + loop, host, username, password, port, web_proto): """Fake the controller constructor.""" mock_device.loop = loop mock_device.host = host diff --git a/tests/components/axis/test_device.py b/tests/components/axis/test_device.py index 2a0a7d6391cb02..72d426819c688f 100644 --- a/tests/components/axis/test_device.py +++ b/tests/components/axis/test_device.py @@ -94,7 +94,7 @@ async def test_new_event_sends_signal(hass): axis_device = device.AxisNetworkDevice(hass, entry) with patch.object(device, 'async_dispatcher_send') as mock_dispatch_send: - axis_device.async_signal_callback(action='add', event='event') + axis_device.async_event_callback(action='add', event='event') await hass.async_block_till_done() assert len(mock_dispatch_send.mock_calls) == 1 From 1050baa9cc04d48137f16ac4074f0d10625ad7e6 Mon Sep 17 00:00:00 2001 From: Kyle Niewiada Date: Fri, 29 Mar 2019 11:08:36 -0400 Subject: [PATCH 240/605] throw `PlatformNotReady` if unable to connect (#22515) Throw `PlatformNotReady` for when the device disconnects, or when the Home Assistant is booting and the ADB server is not ready yet. --- homeassistant/components/androidtv/media_player.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 5bce21f05a0b20..0129b547acf505 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -18,6 +18,7 @@ ATTR_COMMAND, ATTR_ENTITY_ID, CONF_DEVICE_CLASS, CONF_HOST, CONF_NAME, CONF_PORT, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_STANDBY) +from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv ANDROIDTV_DOMAIN = 'androidtv' @@ -125,7 +126,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): _LOGGER.warning("Could not connect to %s at %s%s", device_name, host, adb_log) - return + raise PlatformNotReady if host in hass.data[ANDROIDTV_DOMAIN]: _LOGGER.warning("Platform already setup on %s, skipping", host) From 6dc127780ebffbdd439a3c3b59fa6e524b7882d9 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Mar 2019 11:52:13 -0400 Subject: [PATCH 241/605] Do not use zha default light polling (#22513) * don't use default light polling * review comment --- homeassistant/components/zha/light.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 8b2cd349b9d036..6ba4efa9b0f874 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -11,6 +11,7 @@ from homeassistant.const import STATE_ON from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.event import async_track_time_interval import homeassistant.util.color as color_util from .const import ( DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, COLOR_CHANNEL, @@ -96,11 +97,6 @@ def __init__(self, unique_id, zha_device, channels, **kwargs): self._supported_features |= light.SUPPORT_COLOR self._hs_color = (0, 0) - @property - def should_poll(self) -> bool: - """Poll state from device.""" - return True - @property def is_on(self) -> bool: """Return true if entity is on.""" @@ -157,6 +153,7 @@ async def async_added_to_hass(self): if self._level_channel: await self.async_accept_signal( self._level_channel, SIGNAL_SET_LEVEL, self.set_level) + async_track_time_interval(self.hass, self.refresh, SCAN_INTERVAL) @callback def async_restore_last_state(self, last_state): @@ -247,3 +244,7 @@ async def async_update(self): if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( 'current_level') + + async def refresh(self, time): + """Call async_update at an interval.""" + await self.async_update() From 75eeeae920c7c9c90c17bc7f1746daf3deb61b57 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 09:11:13 -0700 Subject: [PATCH 242/605] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000000000..7ce469f2175d7a --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,56 @@ +# Python package +# Create and test a Python package on multiple Python versions. +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- master + +jobs: + +- job: 'Test' + pool: + vmImage: 'Ubuntu-16.04' + strategy: + matrix: + Python35: + python.version: '3.5.3' + Python36: + python.version: '3.6' + Python37: + python.version: '3.7' + maxParallel: 4 + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + architecture: 'x64' + + - script: python -m pip install --upgrade pip && pip install -r requirements_all.txt + displayName: 'Install dependencies' + + - script: | + pip install pytest + pytest tests + displayName: 'pytest' + + # - task: PublishTestResults@2 + # inputs: + # testResultsFiles: '**/test-results.xml' + # testRunTitle: 'Python $(python.version)' + # condition: succeededOrFailed() + +- job: 'Publish' + dependsOn: 'Test' + pool: + vmImage: 'Ubuntu-16.04' + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.x' + architecture: 'x64' + + - script: python setup.py sdist + displayName: 'Build sdist' From e7d3b22b4629fa0a66a5ffb6e0f515c94f3a0559 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 09:18:25 -0700 Subject: [PATCH 243/605] Add lint task to Azure Pipelines [skip ci] --- azure-pipelines.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7ce469f2175d7a..0fdd8152679fff 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,6 +8,26 @@ trigger: jobs: +- job: 'Lint' + pool: + vmImage: 'Ubuntu-16.04' + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.5.3' + architecture: 'x64' + + - script: python -m pip install --upgrade pip && pip install -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt + displayName: 'Install dependencies' + + - script: | + python script/gen_requirements_all.py validate + flake8 + pydocstyle tests + pylint homeassistant + displayName: 'lint' + - job: 'Test' pool: vmImage: 'Ubuntu-16.04' @@ -27,11 +47,10 @@ jobs: versionSpec: '$(python.version)' architecture: 'x64' - - script: python -m pip install --upgrade pip && pip install -r requirements_all.txt + - script: python -m pip install --upgrade pip && pip install -r requirements_test_all.txt -c homeassistant/package_constraints.txt displayName: 'Install dependencies' - script: | - pip install pytest pytest tests displayName: 'pytest' @@ -52,5 +71,8 @@ jobs: versionSpec: '3.x' architecture: 'x64' + - script: python -m pip install --upgrade pip && pip install wheel + displayName: 'Install dependencies' + - script: python setup.py sdist displayName: 'Build sdist' From ec076c7c10bedb95dc069cf49a77f018ab8e9bde Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 09:21:20 -0700 Subject: [PATCH 244/605] Azure Pipelines: No Python 3.5.3 available, use any 3.5 version [skip ci] --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0fdd8152679fff..1746ca7a8a27d5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,7 +15,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.5.3' + versionSpec: '3.5' architecture: 'x64' - script: python -m pip install --upgrade pip && pip install -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt @@ -34,7 +34,7 @@ jobs: strategy: matrix: Python35: - python.version: '3.5.3' + python.version: '3.5' Python36: python.version: '3.6' Python37: From 4e78d895d9d2887b6c3ecbfa14cf795487b6c2a5 Mon Sep 17 00:00:00 2001 From: zewelor Date: Fri, 29 Mar 2019 18:43:29 +0100 Subject: [PATCH 245/605] Fixes for yeelight availbility state (#22502) --- homeassistant/components/yeelight/__init__.py | 28 +++++-- homeassistant/components/yeelight/light.py | 83 ++++++++----------- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 14b4656c403831..fb218a67698e44 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -212,6 +212,7 @@ def __init__(self, hass, ipaddr, config): self._name = config.get(CONF_NAME) self._model = config.get(CONF_MODEL) self._bulb_device = None + self._available = False @property def bulb(self): @@ -224,7 +225,9 @@ def bulb(self): # force init for type self.update() + self._available = True except yeelight.BulbException as ex: + self._available = False _LOGGER.error("Failed to connect to bulb %s, %s: %s", self._ipaddr, self._name, ex) @@ -245,10 +248,15 @@ def ipaddr(self): """Return ip address.""" return self._ipaddr + @property + def available(self): + """Return true is device is available.""" + return self._available + @property def is_nightlight_enabled(self) -> bool: """Return true / false if nightlight is currently enabled.""" - if self._bulb_device is None: + if self.bulb is None: return False return self.bulb.last_properties.get('active_mode') == '1' @@ -271,7 +279,7 @@ def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None): light_type = yeelight.enums.LightType.Main try: - self._bulb_device.turn_on(duration=duration, light_type=light_type) + self.bulb.turn_on(duration=duration, light_type=light_type) except yeelight.BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return @@ -284,16 +292,24 @@ def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None): light_type = yeelight.enums.LightType.Main try: - self._bulb_device.turn_off(duration=duration, - light_type=light_type) + self.bulb.turn_off(duration=duration, light_type=light_type) except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb on: %s", ex) + _LOGGER.error("Unable to turn the bulb off: %s", ex) return def update(self): """Read new properties from the device.""" + import yeelight + if not self.bulb: return - self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) + try: + self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES) + self._available = True + except yeelight.BulbException as ex: + if self._available: # just inform once + _LOGGER.error("Unable to update bulb status: %s", ex) + self._available = False + dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index cc3810c49685e2..92b668c6987aa6 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -162,7 +162,6 @@ def __init__(self, device, custom_effects=None): self._device = device self._supported_features = SUPPORT_YEELIGHT - self._available = False self._brightness = None self._color_temp = None @@ -196,7 +195,7 @@ def should_poll(self): @property def available(self) -> bool: """Return if bulb is available.""" - return self._available + return self.device.available @property def supported_features(self) -> int: @@ -304,14 +303,7 @@ def _is_nightlight_enabled(self): # F821: https://github.com/PyCQA/pyflakes/issues/373 @property def _bulb(self) -> 'yeelight.Bulb': # noqa: F821 - bulb = self.device.bulb - - if bulb: - self._available = True - return bulb - - self._available = False - return None + return self.device.bulb def set_music_mode(self, mode) -> None: """Set the music mode on or off.""" @@ -323,52 +315,45 @@ def set_music_mode(self, mode) -> None: def update(self) -> None: """Update properties from the bulb.""" import yeelight - try: - bulb_type = self._bulb.bulb_type - - if bulb_type == yeelight.BulbType.Color: - self._supported_features = SUPPORT_YEELIGHT_RGB - elif self.light_type == yeelight.enums.LightType.Ambient: - self._supported_features = SUPPORT_YEELIGHT_RGB - elif bulb_type in (yeelight.BulbType.WhiteTemp, - yeelight.BulbType.WhiteTempMood): - if self._is_nightlight_enabled: - self._supported_features = SUPPORT_YEELIGHT - else: - self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP - - if self.min_mireds is None: - model_specs = self._bulb.get_model_specs() - self._min_mireds = \ - kelvin_to_mired(model_specs['color_temp']['max']) - self._max_mireds = \ - kelvin_to_mired(model_specs['color_temp']['min']) - - if bulb_type == yeelight.BulbType.WhiteTempMood: - self._is_on = self._get_property('main_power') == 'on' - else: - self._is_on = self._get_property('power') == 'on' - + bulb_type = self._bulb.bulb_type + + if bulb_type == yeelight.BulbType.Color: + self._supported_features = SUPPORT_YEELIGHT_RGB + elif self.light_type == yeelight.enums.LightType.Ambient: + self._supported_features = SUPPORT_YEELIGHT_RGB + elif bulb_type in (yeelight.BulbType.WhiteTemp, + yeelight.BulbType.WhiteTempMood): if self._is_nightlight_enabled: - bright = self._get_property('nl_br', None) + self._supported_features = SUPPORT_YEELIGHT else: - bright = self._get_property('bright', None) + self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP - if bright: - self._brightness = round(255 * (int(bright) / 100)) + if self.min_mireds is None: + model_specs = self._bulb.get_model_specs() + self._min_mireds = \ + kelvin_to_mired(model_specs['color_temp']['max']) + self._max_mireds = \ + kelvin_to_mired(model_specs['color_temp']['min']) - temp_in_k = self._get_property('ct') + if bulb_type == yeelight.BulbType.WhiteTempMood: + self._is_on = self._get_property('main_power') == 'on' + else: + self._is_on = self._get_property('power') == 'on' - if temp_in_k: - self._color_temp = kelvin_to_mired(int(temp_in_k)) + if self._is_nightlight_enabled: + bright = self._get_property('nl_br') + else: + bright = self._get_property('bright') - self._hs = self._get_hs_from_properties() + if bright: + self._brightness = round(255 * (int(bright) / 100)) - self._available = True - except yeelight.BulbException as ex: - if self._available: # just inform once - _LOGGER.error("Unable to update bulb status: %s", ex) - self._available = False + temp_in_k = self._get_property('ct') + + if temp_in_k: + self._color_temp = kelvin_to_mired(int(temp_in_k)) + + self._hs = self._get_hs_from_properties() @_cmd def set_brightness(self, brightness, duration) -> None: From c31ab7a17514404bc4493b6eb088242f762dc411 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 29 Mar 2019 11:45:02 -0700 Subject: [PATCH 246/605] Fix tts Great Migration issue (#22539) --- homeassistant/components/amazon_polly/__init__.py | 1 + homeassistant/components/amazon_polly/tts.py | 3 +-- homeassistant/components/baidu/__init__.py | 1 + homeassistant/components/baidu/tts.py | 3 +-- homeassistant/components/marytts/__init__.py | 1 + homeassistant/components/marytts/tts.py | 3 +-- homeassistant/components/microsoft/__init__.py | 1 + homeassistant/components/microsoft/tts.py | 3 +-- homeassistant/components/picotts/__init__.py | 1 + homeassistant/components/picotts/tts.py | 2 +- homeassistant/components/voicerss/__init__.py | 1 + homeassistant/components/voicerss/tts.py | 3 +-- homeassistant/components/yandextts/__init__.py | 1 + homeassistant/components/yandextts/tts.py | 3 +-- requirements_all.txt | 7 +++++++ 15 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 homeassistant/components/amazon_polly/__init__.py create mode 100644 homeassistant/components/baidu/__init__.py create mode 100644 homeassistant/components/marytts/__init__.py create mode 100644 homeassistant/components/microsoft/__init__.py create mode 100644 homeassistant/components/picotts/__init__.py create mode 100644 homeassistant/components/voicerss/__init__.py create mode 100644 homeassistant/components/yandextts/__init__.py diff --git a/homeassistant/components/amazon_polly/__init__.py b/homeassistant/components/amazon_polly/__init__.py new file mode 100644 index 00000000000000..0fab4af43e6f19 --- /dev/null +++ b/homeassistant/components/amazon_polly/__init__.py @@ -0,0 +1 @@ +"""Support for Amazon Polly integration.""" diff --git a/homeassistant/components/amazon_polly/tts.py b/homeassistant/components/amazon_polly/tts.py index 12383df115a33c..167cd9cfc784b0 100644 --- a/homeassistant/components/amazon_polly/tts.py +++ b/homeassistant/components/amazon_polly/tts.py @@ -8,10 +8,9 @@ import voluptuous as vol +from homeassistant.components.tts import PLATFORM_SCHEMA, Provider import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, Provider - REQUIREMENTS = ['boto3==1.9.16'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/baidu/__init__.py b/homeassistant/components/baidu/__init__.py new file mode 100644 index 00000000000000..8a332cf52e143c --- /dev/null +++ b/homeassistant/components/baidu/__init__.py @@ -0,0 +1 @@ +"""Support for Baidu integration.""" diff --git a/homeassistant/components/baidu/tts.py b/homeassistant/components/baidu/tts.py index e7a1f368f1daf3..07b69d41dfd624 100644 --- a/homeassistant/components/baidu/tts.py +++ b/homeassistant/components/baidu/tts.py @@ -9,11 +9,10 @@ import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider - REQUIREMENTS = ["baidu-aip==1.6.6"] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/marytts/__init__.py b/homeassistant/components/marytts/__init__.py new file mode 100644 index 00000000000000..ec85cb6d4ab339 --- /dev/null +++ b/homeassistant/components/marytts/__init__.py @@ -0,0 +1 @@ +"""Support for MaryTTS integration.""" diff --git a/homeassistant/components/marytts/tts.py b/homeassistant/components/marytts/tts.py index 8f6a46b0c3ebdb..f5d19c977a40f2 100644 --- a/homeassistant/components/marytts/tts.py +++ b/homeassistant/components/marytts/tts.py @@ -12,12 +12,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) SUPPORT_LANGUAGES = [ diff --git a/homeassistant/components/microsoft/__init__.py b/homeassistant/components/microsoft/__init__.py new file mode 100644 index 00000000000000..2d281cd2bd85d2 --- /dev/null +++ b/homeassistant/components/microsoft/__init__.py @@ -0,0 +1 @@ +"""Support for Microsoft integration.""" diff --git a/homeassistant/components/microsoft/tts.py b/homeassistant/components/microsoft/tts.py index ab9fb576c2856b..55cf7a4ae7a016 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -9,11 +9,10 @@ import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY, CONF_TYPE import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider - CONF_GENDER = 'gender' CONF_OUTPUT = 'output' CONF_RATE = 'rate' diff --git a/homeassistant/components/picotts/__init__.py b/homeassistant/components/picotts/__init__.py new file mode 100644 index 00000000000000..7ffc80db2f95f2 --- /dev/null +++ b/homeassistant/components/picotts/__init__.py @@ -0,0 +1 @@ +"""Support for pico integration.""" diff --git a/homeassistant/components/picotts/tts.py b/homeassistant/components/picotts/tts.py index 99d3b5e9786516..c164e7fb85dae7 100644 --- a/homeassistant/components/picotts/tts.py +++ b/homeassistant/components/picotts/tts.py @@ -12,7 +12,7 @@ import voluptuous as vol -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/voicerss/__init__.py b/homeassistant/components/voicerss/__init__.py new file mode 100644 index 00000000000000..4894ca30bbdc32 --- /dev/null +++ b/homeassistant/components/voicerss/__init__.py @@ -0,0 +1 @@ +"""Support for VoiceRSS integration.""" diff --git a/homeassistant/components/voicerss/tts.py b/homeassistant/components/voicerss/tts.py index 20e0ee11db3928..436f070e503833 100644 --- a/homeassistant/components/voicerss/tts.py +++ b/homeassistant/components/voicerss/tts.py @@ -11,12 +11,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) VOICERSS_API_URL = "https://api.voicerss.org/" diff --git a/homeassistant/components/yandextts/__init__.py b/homeassistant/components/yandextts/__init__.py new file mode 100644 index 00000000000000..86ac9b58f73d35 --- /dev/null +++ b/homeassistant/components/yandextts/__init__.py @@ -0,0 +1 @@ +"""Support for the yandex speechkit tts integration.""" diff --git a/homeassistant/components/yandextts/tts.py b/homeassistant/components/yandextts/tts.py index 281839a2d74cb6..e60b890e84fcb0 100644 --- a/homeassistant/components/yandextts/tts.py +++ b/homeassistant/components/yandextts/tts.py @@ -11,12 +11,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) YANDEX_API_URL = "https://tts.voicetech.yandex.net/generate?" diff --git a/requirements_all.txt b/requirements_all.txt index 66ad3d044303d9..cad043272bfa9d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -194,6 +194,9 @@ av==6.1.2 # homeassistant.components.axis axis==19 +# homeassistant.components.baidu.tts +baidu-aip==1.6.6 + # homeassistant.components.modem_callerid.sensor basicmodem==0.7 @@ -233,6 +236,7 @@ blockchain==1.4.4 # bme680==1.0.5 # homeassistant.components.route53 +# homeassistant.components.amazon_polly.tts # homeassistant.components.aws_lambda.notify # homeassistant.components.aws_sns.notify # homeassistant.components.aws_sqs.notify @@ -987,6 +991,9 @@ pycomfoconnect==0.3 # homeassistant.components.coolmaster.climate pycoolmasternet==0.0.4 +# homeassistant.components.microsoft.tts +pycsspeechtts==1.0.2 + # homeassistant.components.cups.sensor # pycups==1.9.73 From daf6b01b987f75c84e062c2ad63e392c796e1651 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Fri, 29 Mar 2019 21:10:00 +0200 Subject: [PATCH 247/605] Ring camera improvements (#22526) * Ring camera improvements Expose last_video_id attribute. Fix missing last_video_url Only update last_video_id when video is ready * Fix formatting --- homeassistant/components/ring/camera.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/ring/camera.py b/homeassistant/components/ring/camera.py index 8970e61b1a1fa0..905cbd46158fff 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -157,14 +157,23 @@ def update(self): self._camera.update() self._utcnow = dt_util.utcnow() - last_recording_id = self._camera.last_recording_id + try: + last_event = self._camera.history(limit=1)[0] + except (IndexError, TypeError): + return + + last_recording_id = last_event['id'] + video_status = last_event['recording']['status'] - if self._last_video_id != last_recording_id or \ - self._utcnow >= self._expires_at: + if video_status == 'ready' and \ + (self._last_video_id != last_recording_id or + self._utcnow >= self._expires_at): - _LOGGER.info("Ring DoorBell properties refreshed") + video_url = self._camera.recording_url(last_recording_id) + if video_url: + _LOGGER.info("Ring DoorBell properties refreshed") - # update attributes if new video or if URL has expired - self._last_video_id = self._camera.last_recording_id - self._video_url = self._camera.recording_url(self._last_video_id) - self._expires_at = FORCE_REFRESH_INTERVAL + self._utcnow + # update attributes if new video or if URL has expired + self._last_video_id = last_recording_id + self._video_url = video_url + self._expires_at = FORCE_REFRESH_INTERVAL + self._utcnow From a07919ced25cb1a204c05bbfdb845357267f9723 Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Fri, 29 Mar 2019 12:10:28 -0700 Subject: [PATCH 248/605] PS4 bump to 0.5.2 (#22523) * Bump pyps4 to 0.5.2 * Bump pyps4 to 0.5.2 * Bump pyps4 to 0.5.2 --- homeassistant/components/ps4/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index d5833ae1673296..9183bbe198901d 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyps4-homeassistant==0.5.0'] +REQUIREMENTS = ['pyps4-homeassistant==0.5.2'] async def async_setup(hass, config): diff --git a/requirements_all.txt b/requirements_all.txt index cad043272bfa9d..21939c7ad39d0d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1232,7 +1232,7 @@ pypoint==1.1.1 pypollencom==2.2.3 # homeassistant.components.ps4 -pyps4-homeassistant==0.5.0 +pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch pyqwikswitch==0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 414f8c459194c9..e7695010e26b36 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -227,7 +227,7 @@ pyopenuv==1.0.9 pyotp==2.2.6 # homeassistant.components.ps4 -pyps4-homeassistant==0.5.0 +pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch pyqwikswitch==0.8 From 640192001964db17b8a754a36e98757d7e4af292 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Mar 2019 16:41:04 -0400 Subject: [PATCH 249/605] clean up channel configuration (#22534) --- .../components/zha/core/channels/__init__.py | 36 ++++++++++--------- homeassistant/components/zha/core/device.py | 8 ++--- homeassistant/components/zha/core/gateway.py | 5 +++ 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index d8a3918889d9bd..10370c42c6645e 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -15,7 +15,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from ..helpers import ( bind_configure_reporting, construct_unique_id, - safe_read, get_attr_id_by_name) + safe_read, get_attr_id_by_name, bind_cluster) from ..const import ( REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED, ATTRIBUTE_CHANNEL, EVENT_RELAY_CHANNEL, ZDO_CHANNEL @@ -141,22 +141,24 @@ async def async_configure(self): manufacturer_code = self._zha_device.manufacturer_code if self.cluster.cluster_id >= 0xfc00 and manufacturer_code: manufacturer = manufacturer_code - - skip_bind = False # bind cluster only for the 1st configured attr - for report_config in self._report_config: - attr = report_config.get('attr') - min_report_interval, max_report_interval, change = \ - report_config.get('config') - await bind_configure_reporting( - self._unique_id, self.cluster, attr, - min_report=min_report_interval, - max_report=max_report_interval, - reportable_change=change, - skip_bind=skip_bind, - manufacturer=manufacturer - ) - skip_bind = True - await asyncio.sleep(uniform(0.1, 0.5)) + if self.cluster.bind_only: + await bind_cluster(self._unique_id, self.cluster) + else: + skip_bind = False # bind cluster only for the 1st configured attr + for report_config in self._report_config: + attr = report_config.get('attr') + min_report_interval, max_report_interval, change = \ + report_config.get('config') + await bind_configure_reporting( + self._unique_id, self.cluster, attr, + min_report=min_report_interval, + max_report=max_report_interval, + reportable_change=change, + skip_bind=skip_bind, + manufacturer=manufacturer + ) + skip_bind = True + await asyncio.sleep(uniform(0.1, 0.5)) _LOGGER.debug( "%s: finished channel configuration", self._unique_id diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 0ddb67484c6c65..435ab25acc60df 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -49,7 +49,7 @@ def __init__(self, hass, zigpy_device, zha_gateway): self._zha_gateway = zha_gateway self.cluster_channels = {} self._relay_channels = {} - self._all_channels = {} + self._all_channels = [] self._name = "{} {}".format( self.manufacturer, self.model @@ -135,7 +135,7 @@ def gateway(self): @property def all_channels(self): """Return cluster channels and relay channels for device.""" - return self._all_channels.values() + return self._all_channels @property def available_signal(self): @@ -195,10 +195,10 @@ def add_cluster_channel(self, cluster_channel): if isinstance(cluster_channel, EventRelayChannel): self._relay_channels[cluster_channel.unique_id] = cluster_channel - self._all_channels[cluster_channel.unique_id] = cluster_channel + self._all_channels.append(cluster_channel) else: self.cluster_channels[cluster_channel.name] = cluster_channel - self._all_channels[cluster_channel.name] = cluster_channel + self._all_channels.append(cluster_channel) async def async_configure(self): """Configure the device.""" diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 4f1e24aad5b2e9..71e41c2509b504 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -266,6 +266,11 @@ async def async_device_initialized(self, device, is_new_join): self._hass, self._config, endpoint_id, endpoint, discovery_infos, device, zha_device, is_new_join ) + if endpoint_id != 0: + for cluster in endpoint.in_clusters.values(): + cluster.bind_only = False + for cluster in endpoint.out_clusters.values(): + cluster.bind_only = True if is_new_join: # configure the device From f46a8378b020044ae95c1e72abba2ebaceeedfc0 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Fri, 29 Mar 2019 21:41:13 +0100 Subject: [PATCH 250/605] Fix regression of the xiaomi_aqara config validation (#22435) * Fix regression of the xiaomi_aqara config validation * Make the key optional again * Add base schema * Remove the GW_MAC default --- .../components/xiaomi_aqara/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index e98655f9d76de4..9b113170f8a751 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -61,7 +61,7 @@ }) -GATEWAY_CONFIG_MAC_OPT = vol.Schema({ +GATEWAY_CONFIG = vol.Schema({ vol.Optional(CONF_KEY): vol.All(cv.string, vol.Length(min=16, max=16)), vol.Optional(CONF_HOST): cv.string, @@ -69,12 +69,12 @@ vol.Optional(CONF_DISABLE, default=False): cv.boolean, }) -GATEWAY_CONFIG_MAC_REQ = vol.Schema({ - vol.Required(CONF_KEY): - vol.All(cv.string, vol.Length(min=16, max=16)), - vol.Optional(CONF_HOST): cv.string, - vol.Optional(CONF_PORT, default=9898): cv.port, - vol.Optional(CONF_DISABLE, default=False): cv.boolean, +GATEWAY_CONFIG_MAC_OPTIONAL = GATEWAY_CONFIG.extend({ + vol.Optional(CONF_MAC): GW_MAC, +}) + +GATEWAY_CONFIG_MAC_REQUIRED = GATEWAY_CONFIG.extend({ + vol.Required(CONF_MAC): GW_MAC, }) @@ -97,8 +97,8 @@ def _fix_conf_defaults(config): DOMAIN: vol.Schema({ vol.Optional(CONF_GATEWAYS, default={}): vol.All(cv.ensure_list, vol.Any( - vol.All([GATEWAY_CONFIG_MAC_OPT], vol.Length(max=1)), - vol.All([GATEWAY_CONFIG_MAC_REQ], vol.Length(min=2)) + vol.All([GATEWAY_CONFIG_MAC_OPTIONAL], vol.Length(max=1)), + vol.All([GATEWAY_CONFIG_MAC_REQUIRED], vol.Length(min=2)) ), [_fix_conf_defaults]), vol.Optional(CONF_INTERFACE, default='any'): cv.string, vol.Optional(CONF_DISCOVERY_RETRY, default=3): cv.positive_int From 613c356c5f2a82e18380c1fdf75b0aec66e10007 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Fri, 29 Mar 2019 21:41:50 +0100 Subject: [PATCH 251/605] Upgrade to async_upnp_client==0.14.7 (#22543) --- homeassistant/components/dlna_dmr/media_player.py | 2 +- homeassistant/components/upnp/__init__.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 9cf42bfec603f6..71195d66c696d8 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -32,7 +32,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import get_local_ip -REQUIREMENTS = ['async-upnp-client==0.14.6'] +REQUIREMENTS = ['async-upnp-client==0.14.7'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/upnp/__init__.py b/homeassistant/components/upnp/__init__.py index ce72eff2ba89ab..5f4abcb24c7918 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,7 +23,7 @@ from .const import LOGGER as _LOGGER from .device import Device -REQUIREMENTS = ['async-upnp-client==0.14.6'] +REQUIREMENTS = ['async-upnp-client==0.14.7'] NOTIFICATION_ID = 'upnp_notification' NOTIFICATION_TITLE = 'UPnP/IGD Setup' diff --git a/requirements_all.txt b/requirements_all.txt index 21939c7ad39d0d..65e9f5da939116 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -183,7 +183,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.upnp # homeassistant.components.dlna_dmr.media_player -async-upnp-client==0.14.6 +async-upnp-client==0.14.7 # homeassistant.components.stream av==6.1.2 From c0ce86fa8e90fc2250ac1e5dc33b52bdfad98673 Mon Sep 17 00:00:00 2001 From: damarco Date: Fri, 29 Mar 2019 22:01:51 +0100 Subject: [PATCH 252/605] Bump zigpy (#22545) --- homeassistant/components/zha/__init__.py | 8 ++++---- requirements_all.txt | 8 ++++---- requirements_test_all.txt | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index adc092dcbe1f4c..292b4fde61f9ac 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -27,11 +27,11 @@ from .core.patches import apply_cluster_listener_patch REQUIREMENTS = [ - 'bellows-homeassistant==0.7.1', - 'zigpy-homeassistant==0.3.0', - 'zigpy-xbee-homeassistant==0.1.2', + 'bellows-homeassistant==0.7.2', + 'zigpy-homeassistant==0.3.1', + 'zigpy-xbee-homeassistant==0.1.3', 'zha-quirks==0.0.7', - 'zigpy-deconz==0.1.2' + 'zigpy-deconz==0.1.3' ] DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({ diff --git a/requirements_all.txt b/requirements_all.txt index 65e9f5da939116..aacf7ce0d85e90 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -212,7 +212,7 @@ batinfo==0.4.2 beautifulsoup4==4.7.1 # homeassistant.components.zha -bellows-homeassistant==0.7.1 +bellows-homeassistant==0.7.2 # homeassistant.components.bmw_connected_drive bimmer_connected==0.5.3 @@ -1846,13 +1846,13 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.1.2 +zigpy-deconz==0.1.3 # homeassistant.components.zha -zigpy-homeassistant==0.3.0 +zigpy-homeassistant==0.3.1 # homeassistant.components.zha -zigpy-xbee-homeassistant==0.1.2 +zigpy-xbee-homeassistant==0.1.3 # homeassistant.components.zoneminder zm-py==0.3.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e7695010e26b36..6bf663ab0bc82e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -63,7 +63,7 @@ av==6.1.2 axis==19 # homeassistant.components.zha -bellows-homeassistant==0.7.1 +bellows-homeassistant==0.7.2 # homeassistant.components.caldav.calendar caldav==0.5.0 @@ -319,4 +319,4 @@ vultr==0.1.2 wakeonlan==1.1.6 # homeassistant.components.zha -zigpy-homeassistant==0.3.0 +zigpy-homeassistant==0.3.1 From 9aa5b904c6bf73e30be791a477f573b2874afe5f Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Fri, 29 Mar 2019 21:41:13 +0100 Subject: [PATCH 253/605] Fix regression of the xiaomi_aqara config validation (#22435) * Fix regression of the xiaomi_aqara config validation * Make the key optional again * Add base schema * Remove the GW_MAC default --- .../components/xiaomi_aqara/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index e98655f9d76de4..9b113170f8a751 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -61,7 +61,7 @@ }) -GATEWAY_CONFIG_MAC_OPT = vol.Schema({ +GATEWAY_CONFIG = vol.Schema({ vol.Optional(CONF_KEY): vol.All(cv.string, vol.Length(min=16, max=16)), vol.Optional(CONF_HOST): cv.string, @@ -69,12 +69,12 @@ vol.Optional(CONF_DISABLE, default=False): cv.boolean, }) -GATEWAY_CONFIG_MAC_REQ = vol.Schema({ - vol.Required(CONF_KEY): - vol.All(cv.string, vol.Length(min=16, max=16)), - vol.Optional(CONF_HOST): cv.string, - vol.Optional(CONF_PORT, default=9898): cv.port, - vol.Optional(CONF_DISABLE, default=False): cv.boolean, +GATEWAY_CONFIG_MAC_OPTIONAL = GATEWAY_CONFIG.extend({ + vol.Optional(CONF_MAC): GW_MAC, +}) + +GATEWAY_CONFIG_MAC_REQUIRED = GATEWAY_CONFIG.extend({ + vol.Required(CONF_MAC): GW_MAC, }) @@ -97,8 +97,8 @@ def _fix_conf_defaults(config): DOMAIN: vol.Schema({ vol.Optional(CONF_GATEWAYS, default={}): vol.All(cv.ensure_list, vol.Any( - vol.All([GATEWAY_CONFIG_MAC_OPT], vol.Length(max=1)), - vol.All([GATEWAY_CONFIG_MAC_REQ], vol.Length(min=2)) + vol.All([GATEWAY_CONFIG_MAC_OPTIONAL], vol.Length(max=1)), + vol.All([GATEWAY_CONFIG_MAC_REQUIRED], vol.Length(min=2)) ), [_fix_conf_defaults]), vol.Optional(CONF_INTERFACE, default='any'): cv.string, vol.Optional(CONF_DISCOVERY_RETRY, default=3): cv.positive_int From 21917f4dc4818dab9e24817d439a3c9dd8cfcac4 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 03:09:12 -0700 Subject: [PATCH 254/605] Fix dev branch (#22493) --- .../components/homekit_controller/__init__.py | 3 +-- homeassistant/loader.py | 14 +++++++++++++- tests/helpers/test_entity_component.py | 2 +- tests/test_config_entries.py | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index f9fd0409c9caaf..44af8bffe26a74 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -1,6 +1,5 @@ """Support for Homekit device discovery.""" import logging -import os from homeassistant.components.discovery import SERVICE_HOMEKIT from homeassistant.helpers import discovery @@ -9,7 +8,7 @@ from .config_flow import load_old_pairings from .connection import get_accessory_information, HKDevice from .const import ( - CONTROLLER, HOMEKIT_DIR, KNOWN_DEVICES, PAIRING_FILE + CONTROLLER, KNOWN_DEVICES ) from .const import DOMAIN # noqa: pylint: disable=unused-import diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 7f0d50f93d4973..8ccbcaa33c4475 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -83,7 +83,11 @@ def get_platform(hass, # type: HomeAssistant """ # If the platform has a component, we will limit the platform loading path # to be the same source (custom/built-in). - component = _load_file(hass, platform_name, LOOKUP_PATHS) + if domain not in ['automation', 'mqtt', 'telegram_bot']: + component = _load_file(hass, platform_name, LOOKUP_PATHS) + else: + # avoid load component for legacy platform + component = None # Until we have moved all platforms under their component/own folder, it # can be that the component is None. @@ -99,6 +103,14 @@ def get_platform(hass, # type: HomeAssistant if platform is not None: return platform + # Legacy platform check for automation: components/automation/event.py + if component is None and domain in ['automation', 'mqtt', 'telegram_bot']: + platform = _load_file( + hass, + PLATFORM_FORMAT.format(domain=platform_name, platform=domain), + base_paths + ) + # Legacy platform check for custom: custom_components/light/hue.py # Only check if the component was also in custom components. if component is None or base_paths[0] == PACKAGE_CUSTOM_COMPONENTS: diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index 163261a4b81158..6da3293d597b5c 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -324,7 +324,7 @@ def test_setup_dependencies_platform(hass): loader.set_component(hass, 'test_component2', MockModule('test_component2')) loader.set_component( - hass, 'test_domain.test_component', + hass, 'test_component.test_domain', MockPlatform(dependencies=['test_component', 'test_component2'])) component = EntityComponent(_LOGGER, DOMAIN, hass) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 324db971583548..32532761ccf9f0 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -192,7 +192,7 @@ async def mock_setup_entry_platform(hass, entry, async_add_entities): async_remove_entry=mock_remove_entry )) loader.set_component( - hass, 'light.test', + hass, 'test.light', MockPlatform(async_setup_entry=mock_setup_entry_platform)) MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager) From a95fb809a5269ba2aaa7d54224a108c0f34012cb Mon Sep 17 00:00:00 2001 From: mvn23 Date: Fri, 29 Mar 2019 08:28:50 +0100 Subject: [PATCH 255/605] Update pyotgw to 0.4b3 (#22496) --- homeassistant/components/opentherm_gw/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index acb277c0ef5d0e..1476363c6bd877 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyotgw==0.4b2'] +REQUIREMENTS = ['pyotgw==0.4b3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 4af334dd01ebda..04c5f93ceb0637 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1198,7 +1198,7 @@ pyoppleio==1.0.5 pyota==2.0.5 # homeassistant.components.opentherm_gw -pyotgw==0.4b2 +pyotgw==0.4b3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp From fae8265a37944254181dea19f2f72fc178c16db4 Mon Sep 17 00:00:00 2001 From: zewelor Date: Fri, 29 Mar 2019 18:43:29 +0100 Subject: [PATCH 256/605] Fixes for yeelight availbility state (#22502) --- homeassistant/components/yeelight/__init__.py | 28 +++++-- homeassistant/components/yeelight/light.py | 83 ++++++++----------- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 14b4656c403831..fb218a67698e44 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -212,6 +212,7 @@ def __init__(self, hass, ipaddr, config): self._name = config.get(CONF_NAME) self._model = config.get(CONF_MODEL) self._bulb_device = None + self._available = False @property def bulb(self): @@ -224,7 +225,9 @@ def bulb(self): # force init for type self.update() + self._available = True except yeelight.BulbException as ex: + self._available = False _LOGGER.error("Failed to connect to bulb %s, %s: %s", self._ipaddr, self._name, ex) @@ -245,10 +248,15 @@ def ipaddr(self): """Return ip address.""" return self._ipaddr + @property + def available(self): + """Return true is device is available.""" + return self._available + @property def is_nightlight_enabled(self) -> bool: """Return true / false if nightlight is currently enabled.""" - if self._bulb_device is None: + if self.bulb is None: return False return self.bulb.last_properties.get('active_mode') == '1' @@ -271,7 +279,7 @@ def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None): light_type = yeelight.enums.LightType.Main try: - self._bulb_device.turn_on(duration=duration, light_type=light_type) + self.bulb.turn_on(duration=duration, light_type=light_type) except yeelight.BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return @@ -284,16 +292,24 @@ def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None): light_type = yeelight.enums.LightType.Main try: - self._bulb_device.turn_off(duration=duration, - light_type=light_type) + self.bulb.turn_off(duration=duration, light_type=light_type) except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb on: %s", ex) + _LOGGER.error("Unable to turn the bulb off: %s", ex) return def update(self): """Read new properties from the device.""" + import yeelight + if not self.bulb: return - self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) + try: + self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES) + self._available = True + except yeelight.BulbException as ex: + if self._available: # just inform once + _LOGGER.error("Unable to update bulb status: %s", ex) + self._available = False + dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index cc3810c49685e2..92b668c6987aa6 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -162,7 +162,6 @@ def __init__(self, device, custom_effects=None): self._device = device self._supported_features = SUPPORT_YEELIGHT - self._available = False self._brightness = None self._color_temp = None @@ -196,7 +195,7 @@ def should_poll(self): @property def available(self) -> bool: """Return if bulb is available.""" - return self._available + return self.device.available @property def supported_features(self) -> int: @@ -304,14 +303,7 @@ def _is_nightlight_enabled(self): # F821: https://github.com/PyCQA/pyflakes/issues/373 @property def _bulb(self) -> 'yeelight.Bulb': # noqa: F821 - bulb = self.device.bulb - - if bulb: - self._available = True - return bulb - - self._available = False - return None + return self.device.bulb def set_music_mode(self, mode) -> None: """Set the music mode on or off.""" @@ -323,52 +315,45 @@ def set_music_mode(self, mode) -> None: def update(self) -> None: """Update properties from the bulb.""" import yeelight - try: - bulb_type = self._bulb.bulb_type - - if bulb_type == yeelight.BulbType.Color: - self._supported_features = SUPPORT_YEELIGHT_RGB - elif self.light_type == yeelight.enums.LightType.Ambient: - self._supported_features = SUPPORT_YEELIGHT_RGB - elif bulb_type in (yeelight.BulbType.WhiteTemp, - yeelight.BulbType.WhiteTempMood): - if self._is_nightlight_enabled: - self._supported_features = SUPPORT_YEELIGHT - else: - self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP - - if self.min_mireds is None: - model_specs = self._bulb.get_model_specs() - self._min_mireds = \ - kelvin_to_mired(model_specs['color_temp']['max']) - self._max_mireds = \ - kelvin_to_mired(model_specs['color_temp']['min']) - - if bulb_type == yeelight.BulbType.WhiteTempMood: - self._is_on = self._get_property('main_power') == 'on' - else: - self._is_on = self._get_property('power') == 'on' - + bulb_type = self._bulb.bulb_type + + if bulb_type == yeelight.BulbType.Color: + self._supported_features = SUPPORT_YEELIGHT_RGB + elif self.light_type == yeelight.enums.LightType.Ambient: + self._supported_features = SUPPORT_YEELIGHT_RGB + elif bulb_type in (yeelight.BulbType.WhiteTemp, + yeelight.BulbType.WhiteTempMood): if self._is_nightlight_enabled: - bright = self._get_property('nl_br', None) + self._supported_features = SUPPORT_YEELIGHT else: - bright = self._get_property('bright', None) + self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP - if bright: - self._brightness = round(255 * (int(bright) / 100)) + if self.min_mireds is None: + model_specs = self._bulb.get_model_specs() + self._min_mireds = \ + kelvin_to_mired(model_specs['color_temp']['max']) + self._max_mireds = \ + kelvin_to_mired(model_specs['color_temp']['min']) - temp_in_k = self._get_property('ct') + if bulb_type == yeelight.BulbType.WhiteTempMood: + self._is_on = self._get_property('main_power') == 'on' + else: + self._is_on = self._get_property('power') == 'on' - if temp_in_k: - self._color_temp = kelvin_to_mired(int(temp_in_k)) + if self._is_nightlight_enabled: + bright = self._get_property('nl_br') + else: + bright = self._get_property('bright') - self._hs = self._get_hs_from_properties() + if bright: + self._brightness = round(255 * (int(bright) / 100)) - self._available = True - except yeelight.BulbException as ex: - if self._available: # just inform once - _LOGGER.error("Unable to update bulb status: %s", ex) - self._available = False + temp_in_k = self._get_property('ct') + + if temp_in_k: + self._color_temp = kelvin_to_mired(int(temp_in_k)) + + self._hs = self._get_hs_from_properties() @_cmd def set_brightness(self, brightness, duration) -> None: From 77f7a53d9ff1d96355648f042336165c7d9572a4 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 14:37:44 -0700 Subject: [PATCH 257/605] Remove botocore dependency from credstash script (#22511) * Remove botocore dependency from credstash script * Update requirements_all.txt * Update pylintrc * Update credstash.py --- homeassistant/scripts/credstash.py | 7 +++---- pylintrc | 1 - requirements_all.txt | 3 --- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/homeassistant/scripts/credstash.py b/homeassistant/scripts/credstash.py index 302910c5b08109..6dd9f90197a86b 100644 --- a/homeassistant/scripts/credstash.py +++ b/homeassistant/scripts/credstash.py @@ -4,7 +4,7 @@ from homeassistant.util.yaml import _SECRET_NAMESPACE -REQUIREMENTS = ['credstash==1.15.0', 'botocore==1.7.34'] +REQUIREMENTS = ['credstash==1.15.0'] def run(args): @@ -24,16 +24,15 @@ def run(args): 'value', help="The value to save when putting a secret", nargs='?', default=None) - # pylint: disable=import-error, no-member + # pylint: disable=no-member import credstash - import botocore args = parser.parse_args(args) table = _SECRET_NAMESPACE try: credstash.listSecrets(table=table) - except botocore.errorfactory.ClientError: + except Exception: # pylint: disable=broad-except credstash.createDdbTable(table=table) if args.action == 'list': diff --git a/pylintrc b/pylintrc index a88aabe1936f1a..7d349033f70ed9 100644 --- a/pylintrc +++ b/pylintrc @@ -42,7 +42,6 @@ reports=no [TYPECHECK] # For attrs ignored-classes=_CountingAttr -generated-members=botocore.errorfactory [FORMAT] expected-line-ending-format=LF diff --git a/requirements_all.txt b/requirements_all.txt index 04c5f93ceb0637..a43a04240baf3c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -235,9 +235,6 @@ blockchain==1.4.4 # homeassistant.components.aws_sqs.notify boto3==1.9.16 -# homeassistant.scripts.credstash -botocore==1.7.34 - # homeassistant.components.braviatv.media_player braviarc-homeassistant==0.3.7.dev0 From 9f72764cffd849fe9658d742332ede61a021695c Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 15:33:21 -0700 Subject: [PATCH 258/605] Fix lint on dev (#22512) ## Description: Fix a lint issue in credstash script. **Related issue (if applicable):** fixes # **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io# ## Example entry for `configuration.yaml` (if applicable): ```yaml ``` ## Checklist: - [ ] The code change is tested and works locally. - [ ] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [ ] New files were added to `.coveragerc`. If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- homeassistant/scripts/credstash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/scripts/credstash.py b/homeassistant/scripts/credstash.py index 6dd9f90197a86b..e2950f8d7a0675 100644 --- a/homeassistant/scripts/credstash.py +++ b/homeassistant/scripts/credstash.py @@ -24,7 +24,7 @@ def run(args): 'value', help="The value to save when putting a secret", nargs='?', default=None) - # pylint: disable=no-member + # pylint: disable=import-error, no-member import credstash args = parser.parse_args(args) From 173ef7cac59ab8e01fce02b4783583e9c8b5d0e6 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Mar 2019 11:52:13 -0400 Subject: [PATCH 259/605] Do not use zha default light polling (#22513) * don't use default light polling * review comment --- homeassistant/components/zha/light.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 8b2cd349b9d036..6ba4efa9b0f874 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -11,6 +11,7 @@ from homeassistant.const import STATE_ON from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.event import async_track_time_interval import homeassistant.util.color as color_util from .const import ( DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, COLOR_CHANNEL, @@ -96,11 +97,6 @@ def __init__(self, unique_id, zha_device, channels, **kwargs): self._supported_features |= light.SUPPORT_COLOR self._hs_color = (0, 0) - @property - def should_poll(self) -> bool: - """Poll state from device.""" - return True - @property def is_on(self) -> bool: """Return true if entity is on.""" @@ -157,6 +153,7 @@ async def async_added_to_hass(self): if self._level_channel: await self.async_accept_signal( self._level_channel, SIGNAL_SET_LEVEL, self.set_level) + async_track_time_interval(self.hass, self.refresh, SCAN_INTERVAL) @callback def async_restore_last_state(self, last_state): @@ -247,3 +244,7 @@ async def async_update(self): if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( 'current_level') + + async def refresh(self, time): + """Call async_update at an interval.""" + await self.async_update() From 53595e76d8abcb58e2348f954c9bef1782b51ac7 Mon Sep 17 00:00:00 2001 From: ktnrg45 <38207570+ktnrg45@users.noreply.github.com> Date: Fri, 29 Mar 2019 12:10:28 -0700 Subject: [PATCH 260/605] PS4 bump to 0.5.2 (#22523) * Bump pyps4 to 0.5.2 * Bump pyps4 to 0.5.2 * Bump pyps4 to 0.5.2 --- homeassistant/components/ps4/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index d5833ae1673296..9183bbe198901d 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyps4-homeassistant==0.5.0'] +REQUIREMENTS = ['pyps4-homeassistant==0.5.2'] async def async_setup(hass, config): diff --git a/requirements_all.txt b/requirements_all.txt index a43a04240baf3c..4468c87fde3371 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1222,7 +1222,7 @@ pypoint==1.1.1 pypollencom==2.2.3 # homeassistant.components.ps4 -pyps4-homeassistant==0.5.0 +pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch pyqwikswitch==0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 60d9697ed191f7..965faa6eb5c811 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -227,7 +227,7 @@ pyopenuv==1.0.9 pyotp==2.2.6 # homeassistant.components.ps4 -pyps4-homeassistant==0.5.0 +pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch pyqwikswitch==0.8 From b7bc520a0ea52079af727d68e4a771c6c1f8d1ce Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Mar 2019 16:41:04 -0400 Subject: [PATCH 261/605] clean up channel configuration (#22534) --- .../components/zha/core/channels/__init__.py | 36 ++++++++++--------- homeassistant/components/zha/core/device.py | 8 ++--- homeassistant/components/zha/core/gateway.py | 5 +++ 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index d8a3918889d9bd..10370c42c6645e 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -15,7 +15,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from ..helpers import ( bind_configure_reporting, construct_unique_id, - safe_read, get_attr_id_by_name) + safe_read, get_attr_id_by_name, bind_cluster) from ..const import ( REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED, ATTRIBUTE_CHANNEL, EVENT_RELAY_CHANNEL, ZDO_CHANNEL @@ -141,22 +141,24 @@ async def async_configure(self): manufacturer_code = self._zha_device.manufacturer_code if self.cluster.cluster_id >= 0xfc00 and manufacturer_code: manufacturer = manufacturer_code - - skip_bind = False # bind cluster only for the 1st configured attr - for report_config in self._report_config: - attr = report_config.get('attr') - min_report_interval, max_report_interval, change = \ - report_config.get('config') - await bind_configure_reporting( - self._unique_id, self.cluster, attr, - min_report=min_report_interval, - max_report=max_report_interval, - reportable_change=change, - skip_bind=skip_bind, - manufacturer=manufacturer - ) - skip_bind = True - await asyncio.sleep(uniform(0.1, 0.5)) + if self.cluster.bind_only: + await bind_cluster(self._unique_id, self.cluster) + else: + skip_bind = False # bind cluster only for the 1st configured attr + for report_config in self._report_config: + attr = report_config.get('attr') + min_report_interval, max_report_interval, change = \ + report_config.get('config') + await bind_configure_reporting( + self._unique_id, self.cluster, attr, + min_report=min_report_interval, + max_report=max_report_interval, + reportable_change=change, + skip_bind=skip_bind, + manufacturer=manufacturer + ) + skip_bind = True + await asyncio.sleep(uniform(0.1, 0.5)) _LOGGER.debug( "%s: finished channel configuration", self._unique_id diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 0ddb67484c6c65..435ab25acc60df 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -49,7 +49,7 @@ def __init__(self, hass, zigpy_device, zha_gateway): self._zha_gateway = zha_gateway self.cluster_channels = {} self._relay_channels = {} - self._all_channels = {} + self._all_channels = [] self._name = "{} {}".format( self.manufacturer, self.model @@ -135,7 +135,7 @@ def gateway(self): @property def all_channels(self): """Return cluster channels and relay channels for device.""" - return self._all_channels.values() + return self._all_channels @property def available_signal(self): @@ -195,10 +195,10 @@ def add_cluster_channel(self, cluster_channel): if isinstance(cluster_channel, EventRelayChannel): self._relay_channels[cluster_channel.unique_id] = cluster_channel - self._all_channels[cluster_channel.unique_id] = cluster_channel + self._all_channels.append(cluster_channel) else: self.cluster_channels[cluster_channel.name] = cluster_channel - self._all_channels[cluster_channel.name] = cluster_channel + self._all_channels.append(cluster_channel) async def async_configure(self): """Configure the device.""" diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 4f1e24aad5b2e9..71e41c2509b504 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -266,6 +266,11 @@ async def async_device_initialized(self, device, is_new_join): self._hass, self._config, endpoint_id, endpoint, discovery_infos, device, zha_device, is_new_join ) + if endpoint_id != 0: + for cluster in endpoint.in_clusters.values(): + cluster.bind_only = False + for cluster in endpoint.out_clusters.values(): + cluster.bind_only = True if is_new_join: # configure the device From ab642ca4eb95319b9369dc5c3b44b3e710077fc3 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 29 Mar 2019 11:45:02 -0700 Subject: [PATCH 262/605] Fix tts Great Migration issue (#22539) --- homeassistant/components/amazon_polly/__init__.py | 1 + homeassistant/components/amazon_polly/tts.py | 3 +-- homeassistant/components/baidu/__init__.py | 1 + homeassistant/components/baidu/tts.py | 3 +-- homeassistant/components/marytts/__init__.py | 1 + homeassistant/components/marytts/tts.py | 3 +-- homeassistant/components/microsoft/__init__.py | 1 + homeassistant/components/microsoft/tts.py | 3 +-- homeassistant/components/picotts/__init__.py | 1 + homeassistant/components/picotts/tts.py | 2 +- homeassistant/components/voicerss/__init__.py | 1 + homeassistant/components/voicerss/tts.py | 3 +-- homeassistant/components/yandextts/__init__.py | 1 + homeassistant/components/yandextts/tts.py | 3 +-- requirements_all.txt | 7 +++++++ 15 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 homeassistant/components/amazon_polly/__init__.py create mode 100644 homeassistant/components/baidu/__init__.py create mode 100644 homeassistant/components/marytts/__init__.py create mode 100644 homeassistant/components/microsoft/__init__.py create mode 100644 homeassistant/components/picotts/__init__.py create mode 100644 homeassistant/components/voicerss/__init__.py create mode 100644 homeassistant/components/yandextts/__init__.py diff --git a/homeassistant/components/amazon_polly/__init__.py b/homeassistant/components/amazon_polly/__init__.py new file mode 100644 index 00000000000000..0fab4af43e6f19 --- /dev/null +++ b/homeassistant/components/amazon_polly/__init__.py @@ -0,0 +1 @@ +"""Support for Amazon Polly integration.""" diff --git a/homeassistant/components/amazon_polly/tts.py b/homeassistant/components/amazon_polly/tts.py index 12383df115a33c..167cd9cfc784b0 100644 --- a/homeassistant/components/amazon_polly/tts.py +++ b/homeassistant/components/amazon_polly/tts.py @@ -8,10 +8,9 @@ import voluptuous as vol +from homeassistant.components.tts import PLATFORM_SCHEMA, Provider import homeassistant.helpers.config_validation as cv -from . import PLATFORM_SCHEMA, Provider - REQUIREMENTS = ['boto3==1.9.16'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/baidu/__init__.py b/homeassistant/components/baidu/__init__.py new file mode 100644 index 00000000000000..8a332cf52e143c --- /dev/null +++ b/homeassistant/components/baidu/__init__.py @@ -0,0 +1 @@ +"""Support for Baidu integration.""" diff --git a/homeassistant/components/baidu/tts.py b/homeassistant/components/baidu/tts.py index e7a1f368f1daf3..07b69d41dfd624 100644 --- a/homeassistant/components/baidu/tts.py +++ b/homeassistant/components/baidu/tts.py @@ -9,11 +9,10 @@ import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider - REQUIREMENTS = ["baidu-aip==1.6.6"] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/marytts/__init__.py b/homeassistant/components/marytts/__init__.py new file mode 100644 index 00000000000000..ec85cb6d4ab339 --- /dev/null +++ b/homeassistant/components/marytts/__init__.py @@ -0,0 +1 @@ +"""Support for MaryTTS integration.""" diff --git a/homeassistant/components/marytts/tts.py b/homeassistant/components/marytts/tts.py index 8f6a46b0c3ebdb..f5d19c977a40f2 100644 --- a/homeassistant/components/marytts/tts.py +++ b/homeassistant/components/marytts/tts.py @@ -12,12 +12,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) SUPPORT_LANGUAGES = [ diff --git a/homeassistant/components/microsoft/__init__.py b/homeassistant/components/microsoft/__init__.py new file mode 100644 index 00000000000000..2d281cd2bd85d2 --- /dev/null +++ b/homeassistant/components/microsoft/__init__.py @@ -0,0 +1 @@ +"""Support for Microsoft integration.""" diff --git a/homeassistant/components/microsoft/tts.py b/homeassistant/components/microsoft/tts.py index ab9fb576c2856b..55cf7a4ae7a016 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -9,11 +9,10 @@ import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY, CONF_TYPE import homeassistant.helpers.config_validation as cv -from . import CONF_LANG, PLATFORM_SCHEMA, Provider - CONF_GENDER = 'gender' CONF_OUTPUT = 'output' CONF_RATE = 'rate' diff --git a/homeassistant/components/picotts/__init__.py b/homeassistant/components/picotts/__init__.py new file mode 100644 index 00000000000000..7ffc80db2f95f2 --- /dev/null +++ b/homeassistant/components/picotts/__init__.py @@ -0,0 +1 @@ +"""Support for pico integration.""" diff --git a/homeassistant/components/picotts/tts.py b/homeassistant/components/picotts/tts.py index 99d3b5e9786516..c164e7fb85dae7 100644 --- a/homeassistant/components/picotts/tts.py +++ b/homeassistant/components/picotts/tts.py @@ -12,7 +12,7 @@ import voluptuous as vol -from . import CONF_LANG, PLATFORM_SCHEMA, Provider +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/voicerss/__init__.py b/homeassistant/components/voicerss/__init__.py new file mode 100644 index 00000000000000..4894ca30bbdc32 --- /dev/null +++ b/homeassistant/components/voicerss/__init__.py @@ -0,0 +1 @@ +"""Support for VoiceRSS integration.""" diff --git a/homeassistant/components/voicerss/tts.py b/homeassistant/components/voicerss/tts.py index 20e0ee11db3928..436f070e503833 100644 --- a/homeassistant/components/voicerss/tts.py +++ b/homeassistant/components/voicerss/tts.py @@ -11,12 +11,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) VOICERSS_API_URL = "https://api.voicerss.org/" diff --git a/homeassistant/components/yandextts/__init__.py b/homeassistant/components/yandextts/__init__.py new file mode 100644 index 00000000000000..86ac9b58f73d35 --- /dev/null +++ b/homeassistant/components/yandextts/__init__.py @@ -0,0 +1 @@ +"""Support for the yandex speechkit tts integration.""" diff --git a/homeassistant/components/yandextts/tts.py b/homeassistant/components/yandextts/tts.py index 281839a2d74cb6..e60b890e84fcb0 100644 --- a/homeassistant/components/yandextts/tts.py +++ b/homeassistant/components/yandextts/tts.py @@ -11,12 +11,11 @@ import async_timeout import voluptuous as vol +from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.const import CONF_API_KEY from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider - _LOGGER = logging.getLogger(__name__) YANDEX_API_URL = "https://tts.voicetech.yandex.net/generate?" diff --git a/requirements_all.txt b/requirements_all.txt index 4468c87fde3371..b63c60cffe4604 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -191,6 +191,9 @@ av==6.1.2 # homeassistant.components.axis axis==17 +# homeassistant.components.baidu.tts +baidu-aip==1.6.6 + # homeassistant.components.modem_callerid.sensor basicmodem==0.7 @@ -230,6 +233,7 @@ blockchain==1.4.4 # bme680==1.0.5 # homeassistant.components.route53 +# homeassistant.components.amazon_polly.tts # homeassistant.components.aws_lambda.notify # homeassistant.components.aws_sns.notify # homeassistant.components.aws_sqs.notify @@ -984,6 +988,9 @@ pycomfoconnect==0.3 # homeassistant.components.coolmaster.climate pycoolmasternet==0.0.4 +# homeassistant.components.microsoft.tts +pycsspeechtts==1.0.2 + # homeassistant.components.cups.sensor # pycups==1.9.73 From ae18705c45057410fc164ca70ba3f18fc7e32653 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Fri, 29 Mar 2019 21:41:50 +0100 Subject: [PATCH 263/605] Upgrade to async_upnp_client==0.14.7 (#22543) --- homeassistant/components/dlna_dmr/media_player.py | 2 +- homeassistant/components/upnp/__init__.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 9cf42bfec603f6..71195d66c696d8 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -32,7 +32,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import get_local_ip -REQUIREMENTS = ['async-upnp-client==0.14.6'] +REQUIREMENTS = ['async-upnp-client==0.14.7'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/upnp/__init__.py b/homeassistant/components/upnp/__init__.py index ce72eff2ba89ab..5f4abcb24c7918 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,7 +23,7 @@ from .const import LOGGER as _LOGGER from .device import Device -REQUIREMENTS = ['async-upnp-client==0.14.6'] +REQUIREMENTS = ['async-upnp-client==0.14.7'] NOTIFICATION_ID = 'upnp_notification' NOTIFICATION_TITLE = 'UPnP/IGD Setup' diff --git a/requirements_all.txt b/requirements_all.txt index b63c60cffe4604..4cfd687db99da4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -180,7 +180,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.upnp # homeassistant.components.dlna_dmr.media_player -async-upnp-client==0.14.6 +async-upnp-client==0.14.7 # homeassistant.components.stream av==6.1.2 From 24095c0d7b28a88455d1a478c6ef119649bcd36e Mon Sep 17 00:00:00 2001 From: damarco Date: Fri, 29 Mar 2019 22:01:51 +0100 Subject: [PATCH 264/605] Bump zigpy (#22545) --- homeassistant/components/zha/__init__.py | 8 ++++---- requirements_all.txt | 8 ++++---- requirements_test_all.txt | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index adc092dcbe1f4c..292b4fde61f9ac 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -27,11 +27,11 @@ from .core.patches import apply_cluster_listener_patch REQUIREMENTS = [ - 'bellows-homeassistant==0.7.1', - 'zigpy-homeassistant==0.3.0', - 'zigpy-xbee-homeassistant==0.1.2', + 'bellows-homeassistant==0.7.2', + 'zigpy-homeassistant==0.3.1', + 'zigpy-xbee-homeassistant==0.1.3', 'zha-quirks==0.0.7', - 'zigpy-deconz==0.1.2' + 'zigpy-deconz==0.1.3' ] DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({ diff --git a/requirements_all.txt b/requirements_all.txt index 4cfd687db99da4..237077e908b55b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -209,7 +209,7 @@ batinfo==0.4.2 beautifulsoup4==4.7.1 # homeassistant.components.zha -bellows-homeassistant==0.7.1 +bellows-homeassistant==0.7.2 # homeassistant.components.bmw_connected_drive bimmer_connected==0.5.3 @@ -1843,13 +1843,13 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.1.2 +zigpy-deconz==0.1.3 # homeassistant.components.zha -zigpy-homeassistant==0.3.0 +zigpy-homeassistant==0.3.1 # homeassistant.components.zha -zigpy-xbee-homeassistant==0.1.2 +zigpy-xbee-homeassistant==0.1.3 # homeassistant.components.zoneminder zm-py==0.3.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 965faa6eb5c811..4af0b078c0bd03 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -63,7 +63,7 @@ av==6.1.2 axis==17 # homeassistant.components.zha -bellows-homeassistant==0.7.1 +bellows-homeassistant==0.7.2 # homeassistant.components.caldav.calendar caldav==0.5.0 @@ -319,4 +319,4 @@ vultr==0.1.2 wakeonlan==1.1.6 # homeassistant.components.zha -zigpy-homeassistant==0.3.0 +zigpy-homeassistant==0.3.1 From c4a4af7c29f92a852d2ad8420f8ea1049028006a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 14:07:22 -0700 Subject: [PATCH 265/605] Bumped version to 0.91.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index eacd1812485438..2fb2dc5450a008 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b1' +PATCH_VERSION = '0b2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From e81e5ea796c9f7b88e9a4d7f5db2fab023220b68 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 29 Mar 2019 16:37:45 -0700 Subject: [PATCH 266/605] Set up Circleci workflow (#22519) * Set up Circleci workflow * Update python tag * Add pre-test job to cache the requirements * Upgrade pip itself * Use 3.7 for lint * Parallelize pylint * Tweak run gen_requirements_all * tweak cache key --- .circleci/config.yml | 201 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 173 insertions(+), 28 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 112ce2284dde58..f9eb28bdf4aa86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,67 +3,174 @@ # Check https://circleci.com/docs/2.0/language-python/ for more details # version: 2.1 -jobs: - build: + +executors: + + python: + parameters: + tag: + type: string + default: latest docker: - # specify the version you desire here - # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` - - image: circleci/python:3.7.2 - - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # - image: circleci/postgres:9.4 + - image: circleci/python:<< parameters.tag >> - image: circleci/buildpack-deps:stretch - working_directory: ~/repo - steps: - - checkout +commands: - - run: - name: setup docker prereqs - command: sudo apt-get update && sudo apt-get install -y --no-install-recommends - libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev - libswscale-dev libswresample-dev libavfilter-dev + docker-prereqs: + description: Set up docker prerequisite requirement + steps: + - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends + libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev + libswscale-dev libswresample-dev libavfilter-dev - # Download and cache dependencies, we don't use fallback cache + install-requirements: + description: Set up venv and install requirements python packages with cache support + parameters: + python: + type: string + default: latest + all: + description: pip install -r requirements_all.txt + type: boolean + default: false + test: + description: pip install -r requirements_test.txt + type: boolean + default: false + test_all: + description: pip install -r requirements_test_all.txt + type: boolean + default: false + steps: - restore_cache: keys: - - v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - + - v1-<< parameters.python >>-{{ checksum "homeassistant/package_constraints.txt" }}-<<# parameters.all >>{{ checksum "requirements_all.txt" }}<>-<<# parameters.test >>{{ checksum "requirements_test.txt" }}<>-<<# parameters.test_all >>{{ checksum "requirements_test_all.txt" }}<> - run: name: install dependencies command: | python3 -m venv venv . venv/bin/activate - pip install -q --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt - + pip install -U pip + <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> + <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> + <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> - save_cache: paths: - ./venv - key: v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} + key: v1-<< parameters.python >>-{{ checksum "homeassistant/package_constraints.txt" }}-<<# parameters.all >>{{ checksum "requirements_all.txt" }}<>-<<# parameters.test >>{{ checksum "requirements_test.txt" }}<>-<<# parameters.test_all >>{{ checksum "requirements_test_all.txt" }}<> + install: + description: Install Home Assistant + steps: - run: name: install command: | . venv/bin/activate pip install --progress-bar off -e . +jobs: + + static-check: + executor: + name: python + tag: 3.7-stretch + + steps: + - checkout + - docker-prereqs + - run: - name: run lint + name: run static check command: | + python3 -m venv venv . venv/bin/activate - python script/gen_requirements_all.py validate + pip install -U pip + pip install --progress-bar off flake8 flake8 - pylint homeassistant + + - install + - run: + name: run gen_requirements_all + command: | + . venv/bin/activate + python script/gen_requirements_all.py validate + + pre-install-all-requirements: + executor: + name: python + tag: 3.7-stretch + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: 3.7-stretch + all: true + test: true + + pylint: + executor: + name: python + tag: 3.7-stretch + parallelism: 3 + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: 3.7-stretch + all: true + test: true + - install + + - run: + name: run pylint + command: | + . venv/bin/activate + PYFILES=$(circleci tests glob "homeassistant/**/*.py" | circleci tests split) + pylint ${PYFILES} + + pre-test: + parameters: + python: + type: string + executor: + name: python + tag: << parameters.python >> + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: << parameters.python >> + test_all: true + + test: + parameters: + python: + type: string + executor: + name: python + tag: << parameters.python >> + parallelism: 3 + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: << parameters.python >> + test_all: true + - install - run: name: run tests command: | . venv/bin/activate + TESTFILES=$(circleci tests glob "tests/**/test_*.py" | circleci tests split --split-by=timings) if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi - pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH + pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty when: always @@ -77,3 +184,41 @@ jobs: - store_artifacts: path: test-reports destination: test-reports + +workflows: + version: 2 + build: + jobs: + - static-check + - pre-install-all-requirements + - pylint: + requires: + - pre-install-all-requirements + - pre-test: + name: pre-test 3.5.5 + python: 3.5.5-stretch + - pre-test: + name: pre-test 3.6 + python: 3.6-stretch + - pre-test: + name: pre-test 3.7 + python: 3.7-stretch + - test: + name: test 3.5.5 + requires: + - pre-test 3.5.5 + python: 3.5.5-stretch + - test: + name: test 3.6 + requires: + - pre-test 3.6 + python: 3.6-stretch + - test: + name: test 3.7 + requires: + - pre-test 3.7 + python: 3.7-stretch + # CircleCI does not allow failure yet + # - test: + # name: test 3.8 + # python: 3.8-rc-stretch From fbb9097f6cd152a99c83939a638d050f83b89cc4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 16:46:15 -0700 Subject: [PATCH 267/605] Updated frontend to 20190329.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 80b5b744488f67..3baea2008b144b 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190327.0'] +REQUIREMENTS = ['home-assistant-frontend==20190329.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index aacf7ce0d85e90..cb9ce3cc04c484 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -551,7 +551,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190327.0 +home-assistant-frontend==20190329.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6bf663ab0bc82e..45a51ec6dcbcfc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -129,7 +129,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190327.0 +home-assistant-frontend==20190329.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 65c47824a0863cc74f75ca20553871d9c775132e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 16:46:15 -0700 Subject: [PATCH 268/605] Updated frontend to 20190329.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 80b5b744488f67..3baea2008b144b 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190327.0'] +REQUIREMENTS = ['home-assistant-frontend==20190329.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 237077e908b55b..c0b458119e2c4c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -548,7 +548,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190327.0 +home-assistant-frontend==20190329.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4af0b078c0bd03..003c9fa43cbb65 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -129,7 +129,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190327.0 +home-assistant-frontend==20190329.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From c05bff7d17c121a39cfadef9c58156f562c1445e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:03:02 -0700 Subject: [PATCH 269/605] Add support for streaming to ffmpeg (#22549) --- homeassistant/components/ffmpeg/camera.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index dbb51bf27c7caf..d897293124bee6 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -9,7 +9,8 @@ import voluptuous as vol -from homeassistant.components.camera import PLATFORM_SCHEMA, Camera +from homeassistant.components.camera import ( + PLATFORM_SCHEMA, Camera, SUPPORT_STREAM) from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream import homeassistant.helpers.config_validation as cv @@ -46,6 +47,16 @@ def __init__(self, hass, config): self._input = config.get(CONF_INPUT) self._extra_arguments = config.get(CONF_EXTRA_ARGUMENTS) + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + return self._input.split(' ')[-1] + async def async_camera_image(self): """Return a still image response from the camera.""" from haffmpeg.tools import ImageFrame, IMAGE_JPEG From cc886821bc4c0e4bacbbe12f91fd7e53d087d728 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:04:59 -0700 Subject: [PATCH 270/605] Fix platform warnings (#22551) --- homeassistant/loader.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 8ccbcaa33c4475..4ca19935206257 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -37,6 +37,7 @@ PACKAGE_CUSTOM_COMPONENTS = 'custom_components' PACKAGE_BUILTIN = 'homeassistant.components' LOOKUP_PATHS = [PACKAGE_CUSTOM_COMPONENTS, PACKAGE_BUILTIN] +COMPONENTS_WITH_BAD_PLATFORMS = ['automation', 'mqtt', 'telegram_bot'] class LoaderError(Exception): @@ -83,7 +84,7 @@ def get_platform(hass, # type: HomeAssistant """ # If the platform has a component, we will limit the platform loading path # to be the same source (custom/built-in). - if domain not in ['automation', 'mqtt', 'telegram_bot']: + if domain not in COMPONENTS_WITH_BAD_PLATFORMS: component = _load_file(hass, platform_name, LOOKUP_PATHS) else: # avoid load component for legacy platform @@ -104,7 +105,7 @@ def get_platform(hass, # type: HomeAssistant return platform # Legacy platform check for automation: components/automation/event.py - if component is None and domain in ['automation', 'mqtt', 'telegram_bot']: + if component is None and domain in COMPONENTS_WITH_BAD_PLATFORMS: platform = _load_file( hass, PLATFORM_FORMAT.format(domain=platform_name, platform=domain), @@ -129,10 +130,11 @@ def get_platform(hass, # type: HomeAssistant _LOGGER.error("Unable to find platform %s.%s", platform_name, extra) return None - _LOGGER.error( - "Integrations need to be in their own folder. Change %s/%s.py to " - "%s/%s.py. This will stop working soon.", - domain, platform_name, platform_name, domain) + if domain not in COMPONENTS_WITH_BAD_PLATFORMS: + _LOGGER.error( + "Integrations need to be in their own folder. Change %s/%s.py to " + "%s/%s.py. This will stop working soon.", + domain, platform_name, platform_name, domain) return platform From f9f100b57590aa9058610ef4d8586ca7a1c21af4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:03:02 -0700 Subject: [PATCH 271/605] Add support for streaming to ffmpeg (#22549) --- homeassistant/components/ffmpeg/camera.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index dbb51bf27c7caf..d897293124bee6 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -9,7 +9,8 @@ import voluptuous as vol -from homeassistant.components.camera import PLATFORM_SCHEMA, Camera +from homeassistant.components.camera import ( + PLATFORM_SCHEMA, Camera, SUPPORT_STREAM) from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream import homeassistant.helpers.config_validation as cv @@ -46,6 +47,16 @@ def __init__(self, hass, config): self._input = config.get(CONF_INPUT) self._extra_arguments = config.get(CONF_EXTRA_ARGUMENTS) + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + return self._input.split(' ')[-1] + async def async_camera_image(self): """Return a still image response from the camera.""" from haffmpeg.tools import ImageFrame, IMAGE_JPEG From 3ad4419cb6da23d708e6678f32606421a38a300e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:04:59 -0700 Subject: [PATCH 272/605] Fix platform warnings (#22551) --- homeassistant/loader.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 8ccbcaa33c4475..4ca19935206257 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -37,6 +37,7 @@ PACKAGE_CUSTOM_COMPONENTS = 'custom_components' PACKAGE_BUILTIN = 'homeassistant.components' LOOKUP_PATHS = [PACKAGE_CUSTOM_COMPONENTS, PACKAGE_BUILTIN] +COMPONENTS_WITH_BAD_PLATFORMS = ['automation', 'mqtt', 'telegram_bot'] class LoaderError(Exception): @@ -83,7 +84,7 @@ def get_platform(hass, # type: HomeAssistant """ # If the platform has a component, we will limit the platform loading path # to be the same source (custom/built-in). - if domain not in ['automation', 'mqtt', 'telegram_bot']: + if domain not in COMPONENTS_WITH_BAD_PLATFORMS: component = _load_file(hass, platform_name, LOOKUP_PATHS) else: # avoid load component for legacy platform @@ -104,7 +105,7 @@ def get_platform(hass, # type: HomeAssistant return platform # Legacy platform check for automation: components/automation/event.py - if component is None and domain in ['automation', 'mqtt', 'telegram_bot']: + if component is None and domain in COMPONENTS_WITH_BAD_PLATFORMS: platform = _load_file( hass, PLATFORM_FORMAT.format(domain=platform_name, platform=domain), @@ -129,10 +130,11 @@ def get_platform(hass, # type: HomeAssistant _LOGGER.error("Unable to find platform %s.%s", platform_name, extra) return None - _LOGGER.error( - "Integrations need to be in their own folder. Change %s/%s.py to " - "%s/%s.py. This will stop working soon.", - domain, platform_name, platform_name, domain) + if domain not in COMPONENTS_WITH_BAD_PLATFORMS: + _LOGGER.error( + "Integrations need to be in their own folder. Change %s/%s.py to " + "%s/%s.py. This will stop working soon.", + domain, platform_name, platform_name, domain) return platform From b1a6539290057af792ecf82407196c46e2cfa368 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 17:05:40 -0700 Subject: [PATCH 273/605] Bumped version to 0.91.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2fb2dc5450a008..48476c4fa909c2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b2' +PATCH_VERSION = '0b3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 8e975395becb39f1fbee74666911cbf0dfc26d28 Mon Sep 17 00:00:00 2001 From: Julien Roy Date: Sat, 30 Mar 2019 03:22:28 +0100 Subject: [PATCH 274/605] upgrade pylinky to 0.3.3 (#22544) --- homeassistant/components/linky/sensor.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/linky/sensor.py b/homeassistant/components/linky/sensor.py index 46e7ed92f45abf..7a10513016f125 100644 --- a/homeassistant/components/linky/sensor.py +++ b/homeassistant/components/linky/sensor.py @@ -17,7 +17,7 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pylinky==0.3.0'] +REQUIREMENTS = ['pylinky==0.3.3'] _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(minutes=10) diff --git a/requirements_all.txt b/requirements_all.txt index cb9ce3cc04c484..aeb88da5b7abeb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1132,7 +1132,7 @@ pylgnetcast-homeassistant==0.2.0.dev0 pylgtv==0.1.9 # homeassistant.components.linky.sensor -pylinky==0.3.0 +pylinky==0.3.3 # homeassistant.components.litejet pylitejet==0.1 From 95a7077b41e6fa3d5529083c10c1a3da2b940a15 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Mar 2019 20:48:45 -0700 Subject: [PATCH 275/605] Move core services.yaml file to Home Assistant integration (#22489) * Move services.yaml to correct dir * Remove special case for HA servicesgs --- .../components/homeassistant/services.yaml | 39 +++++++++++++++++++ homeassistant/components/services.yaml | 35 ----------------- homeassistant/helpers/service.py | 12 +----- 3 files changed, 41 insertions(+), 45 deletions(-) create mode 100644 homeassistant/components/homeassistant/services.yaml delete mode 100644 homeassistant/components/services.yaml diff --git a/homeassistant/components/homeassistant/services.yaml b/homeassistant/components/homeassistant/services.yaml new file mode 100644 index 00000000000000..2219564abb875a --- /dev/null +++ b/homeassistant/components/homeassistant/services.yaml @@ -0,0 +1,39 @@ +check_config: + description: Check the Home Assistant configuration files for errors. Errors will be displayed in the Home Assistant log. + +reload_core_config: + description: Reload the core configuration. + +restart: + description: Restart the Home Assistant service. + +stop: + description: Stop the Home Assistant service. + +toggle: + description: Generic service to toggle devices on/off under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. + fields: + entity_id: + description: The entity_id of the device to toggle on/off. + example: light.living_room + +turn_on: + description: Generic service to turn devices on under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. + fields: + entity_id: + description: The entity_id of the device to turn on. + example: light.living_room + +turn_off: + description: Generic service to turn devices off under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. + fields: + entity_id: + description: The entity_id of the device to turn off. + example: light.living_room + +update_entity: + description: Force one or more entities to update its data + fields: + entity_id: + description: One or multiple entity_ids to update. Can be a list. + example: light.living_room diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml deleted file mode 100644 index 40c7637653101f..00000000000000 --- a/homeassistant/components/services.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Describes the format for available component services - -homeassistant: - check_config: - description: Check the Home Assistant configuration files for errors. Errors will be displayed in the Home Assistant log. - reload_core_config: - description: Reload the core configuration. - restart: - description: Restart the Home Assistant service. - stop: - description: Stop the Home Assistant service. - toggle: - description: Generic service to toggle devices on/off under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. - fields: - entity_id: - description: The entity_id of the device to toggle on/off. - example: light.living_room - turn_on: - description: Generic service to turn devices on under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. - fields: - entity_id: - description: The entity_id of the device to turn on. - example: light.living_room - turn_off: - description: Generic service to turn devices off under any domain. Same usage as the light.turn_on, switch.turn_on, etc. services. - fields: - entity_id: - description: The entity_id of the device to turn off. - example: light.living_room - update_entity: - description: Force one or more entities to update its data - fields: - entity_id: - description: One or multiple entity_ids to update. Can be a list. - example: light.living_room diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 43b8318abc5353..f8af3bdb1c57c3 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -163,11 +163,7 @@ async def async_get_all_descriptions(hass): def domain_yaml_file(domain): """Return the services.yaml location for a domain.""" - if domain == ha.DOMAIN: - from homeassistant import components - component_path = path.dirname(components.__file__) - else: - component_path = path.dirname(get_component(hass, domain).__file__) + component_path = path.dirname(get_component(hass, domain).__file__) return path.join(component_path, 'services.yaml') def load_services_files(yaml_files): @@ -195,7 +191,6 @@ def load_services_files(yaml_files): loaded = await hass.async_add_job(load_services_files, missing) # Build response - catch_all_yaml_file = domain_yaml_file(ha.DOMAIN) descriptions = {} for domain in services: descriptions[domain] = {} @@ -207,10 +202,7 @@ def load_services_files(yaml_files): # Cache missing descriptions if description is None: - if yaml_file == catch_all_yaml_file: - yaml_services = loaded[yaml_file].get(domain, {}) - else: - yaml_services = loaded[yaml_file] + yaml_services = loaded[yaml_file] yaml_description = yaml_services.get(service, {}) description = description_cache[cache_key] = { From b04fd08cea798a9b8c5c91a1414900f62a5bb171 Mon Sep 17 00:00:00 2001 From: giefca Date: Sat, 30 Mar 2019 04:51:47 +0100 Subject: [PATCH 276/605] Google assistant: add blinds trait for covers (#22336) * Update const.py * Update smart_home.py * Update trait.py * Update test_trait.py * Update smart_home.py * Update test_trait.py * Update trait.py * Update trait.py * Update test_trait.py * Update test_trait.py * Update __init__.py * Update test_trait.py * Change email * Trying to correct CLA * Update __init__.py * Update trait.py * Update trait.py * Update trait.py * Update trait.py * Update __init__.py * Update test_trait.py * Update test_google_assistant.py * Update trait.py * Update trait.py * Update test_trait.py * Update test_trait.py --- .../components/google_assistant/const.py | 1 + .../components/google_assistant/smart_home.py | 4 +- .../components/google_assistant/trait.py | 92 +++++++++++++----- tests/components/google_assistant/__init__.py | 20 ++-- .../components/google_assistant/test_trait.py | 94 ++++++------------- 5 files changed, 110 insertions(+), 101 deletions(-) diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index 543404dd34e358..852ea2469a2ff2 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -29,6 +29,7 @@ TYPE_FAN = PREFIX_TYPES + 'FAN' TYPE_THERMOSTAT = PREFIX_TYPES + 'THERMOSTAT' TYPE_LOCK = PREFIX_TYPES + 'LOCK' +TYPE_BLINDS = PREFIX_TYPES + 'BLINDS' SERVICE_REQUEST_SYNC = 'request_sync' HOMEGRAPH_URL = 'https://homegraph.googleapis.com/' diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index 88cbea345b1b82..d84c8037c60bef 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -31,7 +31,7 @@ from . import trait from .const import ( TYPE_LIGHT, TYPE_LOCK, TYPE_SCENE, TYPE_SWITCH, TYPE_VACUUM, - TYPE_THERMOSTAT, TYPE_FAN, TYPE_CAMERA, + TYPE_THERMOSTAT, TYPE_FAN, TYPE_CAMERA, TYPE_BLINDS, CONF_ALIASES, CONF_ROOM_HINT, ERR_FUNCTION_NOT_SUPPORTED, ERR_PROTOCOL_ERROR, ERR_DEVICE_OFFLINE, ERR_UNKNOWN_ERROR, @@ -45,7 +45,7 @@ DOMAIN_TO_GOOGLE_TYPES = { camera.DOMAIN: TYPE_CAMERA, climate.DOMAIN: TYPE_THERMOSTAT, - cover.DOMAIN: TYPE_SWITCH, + cover.DOMAIN: TYPE_BLINDS, fan.DOMAIN: TYPE_FAN, group.DOMAIN: TYPE_SWITCH, input_boolean.DOMAIN: TYPE_SWITCH, diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index bd903575762ff3..81918ff2e886ad 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -48,6 +48,7 @@ TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock' TRAIT_FANSPEED = PREFIX_TRAITS + 'FanSpeed' TRAIT_MODES = PREFIX_TRAITS + 'Modes' +TRAIT_OPENCLOSE = PREFIX_TRAITS + 'OpenClose' PREFIX_COMMANDS = 'action.devices.commands.' COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff' @@ -66,6 +67,7 @@ COMMAND_LOCKUNLOCK = PREFIX_COMMANDS + 'LockUnlock' COMMAND_FANSPEED = PREFIX_COMMANDS + 'SetFanSpeed' COMMAND_MODES = PREFIX_COMMANDS + 'SetModes' +COMMAND_OPENCLOSE = PREFIX_COMMANDS + 'OpenClose' TRAITS = [] @@ -128,8 +130,6 @@ def supported(domain, features): """Test if state is supported.""" if domain == light.DOMAIN: return features & light.SUPPORT_BRIGHTNESS - if domain == cover.DOMAIN: - return features & cover.SUPPORT_SET_POSITION if domain == media_player.DOMAIN: return features & media_player.SUPPORT_VOLUME_SET @@ -149,11 +149,6 @@ def query_attributes(self): if brightness is not None: response['brightness'] = int(100 * (brightness / 255)) - elif domain == cover.DOMAIN: - position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) - if position is not None: - response['brightness'] = position - elif domain == media_player.DOMAIN: level = self.state.attributes.get( media_player.ATTR_MEDIA_VOLUME_LEVEL) @@ -173,12 +168,6 @@ async def execute(self, command, data, params): ATTR_ENTITY_ID: self.state.entity_id, light.ATTR_BRIGHTNESS_PCT: params['brightness'] }, blocking=True, context=data.context) - elif domain == cover.DOMAIN: - await self.hass.services.async_call( - cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, { - ATTR_ENTITY_ID: self.state.entity_id, - cover.ATTR_POSITION: params['brightness'] - }, blocking=True, context=data.context) elif domain == media_player.DOMAIN: await self.hass.services.async_call( media_player.DOMAIN, media_player.SERVICE_VOLUME_SET, { @@ -254,7 +243,6 @@ def supported(domain, features): switch.DOMAIN, fan.DOMAIN, light.DOMAIN, - cover.DOMAIN, media_player.DOMAIN, ) @@ -264,22 +252,13 @@ def sync_attributes(self): def query_attributes(self): """Return OnOff query attributes.""" - if self.state.domain == cover.DOMAIN: - return {'on': self.state.state != cover.STATE_CLOSED} return {'on': self.state.state != STATE_OFF} async def execute(self, command, data, params): """Execute an OnOff command.""" domain = self.state.domain - if domain == cover.DOMAIN: - service_domain = domain - if params['on']: - service = cover.SERVICE_OPEN_COVER - else: - service = cover.SERVICE_CLOSE_COVER - - elif domain == group.DOMAIN: + if domain == group.DOMAIN: service_domain = HA_DOMAIN service = SERVICE_TURN_ON if params['on'] else SERVICE_TURN_OFF @@ -1047,3 +1026,68 @@ async def execute(self, command, data, params): ATTR_ENTITY_ID: self.state.entity_id, media_player.ATTR_INPUT_SOURCE: source }, blocking=True, context=data.context) + + +@register_trait +class OpenCloseTrait(_Trait): + """Trait to open and close a cover. + + https://developers.google.com/actions/smarthome/traits/openclose + """ + + name = TRAIT_OPENCLOSE + commands = [ + COMMAND_OPENCLOSE + ] + + @staticmethod + def supported(domain, features): + """Test if state is supported.""" + return domain == cover.DOMAIN + + def sync_attributes(self): + """Return opening direction.""" + return {} + + def query_attributes(self): + """Return state query attributes.""" + domain = self.state.domain + response = {} + + if domain == cover.DOMAIN: + position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) + if position is not None: + response['openPercent'] = position + else: + if self.state.state != cover.STATE_CLOSED: + response['openPercent'] = 100 + else: + response['openPercent'] = 0 + + return response + + async def execute(self, command, data, params): + """Execute an Open, close, Set position command.""" + domain = self.state.domain + + if domain == cover.DOMAIN: + position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) + if position is not None: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, { + ATTR_ENTITY_ID: self.state.entity_id, + cover.ATTR_POSITION: params['openPercent'] + }, blocking=True, context=data.context) + else: + if self.state.state != cover.STATE_CLOSED: + if params['openPercent'] < 100: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_CLOSE_COVER, { + ATTR_ENTITY_ID: self.state.entity_id + }, blocking=True, context=data.context) + else: + if params['openPercent'] > 0: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_OPEN_COVER, { + ATTR_ENTITY_ID: self.state.entity_id + }, blocking=True, context=data.context) diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index a8ea4a3f8881d0..331c6d2d9f5082 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -1,3 +1,5 @@ + + """Tests for the Google Assistant integration.""" DEMO_DEVICES = [{ @@ -93,9 +95,9 @@ 'name': 'Living Room Window' }, 'traits': - ['action.devices.traits.OnOff', 'action.devices.traits.Brightness'], + ['action.devices.traits.OpenClose'], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.BLINDS', 'willReportState': False }, { @@ -105,9 +107,9 @@ 'name': 'Hall Window' }, 'traits': - ['action.devices.traits.OnOff', 'action.devices.traits.Brightness'], + ['action.devices.traits.OpenClose'], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.BLINDS', 'willReportState': False }, { @@ -115,16 +117,18 @@ 'name': { 'name': 'Garage Door' }, - 'traits': ['action.devices.traits.OnOff'], - 'type': 'action.devices.types.SWITCH', + 'traits': ['action.devices.traits.OpenClose'], + 'type': + 'action.devices.types.BLINDS', 'willReportState': False }, { 'id': 'cover.kitchen_window', 'name': { 'name': 'Kitchen Window' }, - 'traits': ['action.devices.traits.OnOff'], - 'type': 'action.devices.types.SWITCH', + 'traits': ['action.devices.traits.OpenClose'], + 'type': + 'action.devices.types.BLINDS', 'willReportState': False }, { 'id': 'group.all_covers', diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index e42e4bdc91505d..a0a710d3d8ce27 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -83,33 +83,6 @@ async def test_brightness_light(hass): } -async def test_brightness_cover(hass): - """Test brightness trait support for cover domain.""" - assert trait.BrightnessTrait.supported(cover.DOMAIN, - cover.SUPPORT_SET_POSITION) - - trt = trait.BrightnessTrait(hass, State('cover.bla', cover.STATE_OPEN, { - cover.ATTR_CURRENT_POSITION: 75 - }), BASIC_CONFIG) - - assert trt.sync_attributes() == {} - - assert trt.query_attributes() == { - 'brightness': 75 - } - - calls = async_mock_service( - hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION) - await trt.execute( - trait.COMMAND_BRIGHTNESS_ABSOLUTE, BASIC_DATA, - {'brightness': 50}) - assert len(calls) == 1 - assert calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - cover.ATTR_POSITION: 50 - } - - async def test_brightness_media_player(hass): """Test brightness trait support for media player domain.""" assert trait.BrightnessTrait.supported(media_player.DOMAIN, @@ -358,46 +331,6 @@ async def test_onoff_light(hass): } -async def test_onoff_cover(hass): - """Test OnOff trait support for cover domain.""" - assert trait.OnOffTrait.supported(cover.DOMAIN, 0) - - trt_on = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_OPEN), - BASIC_CONFIG) - - assert trt_on.sync_attributes() == {} - - assert trt_on.query_attributes() == { - 'on': True - } - - trt_off = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_CLOSED), - BASIC_CONFIG) - - assert trt_off.query_attributes() == { - 'on': False - } - - on_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': True}) - assert len(on_calls) == 1 - assert on_calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - } - - off_calls = async_mock_service(hass, cover.DOMAIN, - cover.SERVICE_CLOSE_COVER) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': False}) - assert len(off_calls) == 1 - assert off_calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - } - - async def test_onoff_media_player(hass): """Test OnOff trait support for media_player domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) @@ -1119,3 +1052,30 @@ async def test_modes(hass): 'entity_id': 'media_player.living_room', 'source': 'media' } + + +async def test_openclose_cover(hass): + """Test cover trait.""" + assert trait.OpenCloseTrait.supported(cover.DOMAIN, + cover.SUPPORT_SET_POSITION) + + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + cover.ATTR_CURRENT_POSITION: 75 + }), BASIC_CONFIG) + + assert trt.sync_attributes() == {} + + assert trt.query_attributes() == { + 'openPercent': 75 + } + + calls = async_mock_service( + hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION) + await trt.execute( + trait.COMMAND_OPENCLOSE, BASIC_DATA, + {'openPercent': 50}) + assert len(calls) == 1 + assert calls[0].data == { + ATTR_ENTITY_ID: 'cover.bla', + cover.ATTR_POSITION: 50 + } From fe8e51e2e946b9f058f52295db209192b1f7d6a4 Mon Sep 17 00:00:00 2001 From: drjared88 Date: Fri, 29 Mar 2019 21:53:01 -0600 Subject: [PATCH 277/605] Update Amcrest component to SUPPORT_STREAM (#22553) * Update camera.py Update Amcrest component to SUPPORT_STREAM to allow streaming in the UI and Google Assistant. * Update camera.py --- homeassistant/components/amcrest/camera.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 35d5e18fdd3505..63c2c720781a2b 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -2,7 +2,8 @@ import asyncio import logging -from homeassistant.components.camera import Camera +from homeassistant.components.camera import ( + Camera, SUPPORT_STREAM) from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import ( @@ -98,6 +99,11 @@ def name(self): """Return the name of this camera.""" return self._name + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + @property def stream_source(self): """Return the source of the stream.""" From 1bfe86b30ddb0e7bc83776ba09a19853569136e9 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Fri, 29 Mar 2019 23:10:00 -0500 Subject: [PATCH 278/605] Change HEOS component library and add basic config flow (#22517) * Update heos lib * Update requirements files * Removed unecessary mock_coro usage * Remove assert_called_once usage * Updates from review feedback * Remove extra param to error format --- .coveragerc | 1 - CODEOWNERS | 1 + .../components/heos/.translations/en.json | 5 + homeassistant/components/heos/__init__.py | 78 +++-- homeassistant/components/heos/config_flow.py | 25 ++ homeassistant/components/heos/const.py | 4 + homeassistant/components/heos/media_player.py | 301 ++++++++++++------ homeassistant/components/heos/strings.json | 5 + requirements_all.txt | 6 +- requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/heos/__init__.py | 1 + tests/components/heos/conftest.py | 67 ++++ tests/components/heos/test_init.py | 104 ++++++ tests/components/heos/test_media_player.py | 180 +++++++++++ 15 files changed, 663 insertions(+), 119 deletions(-) create mode 100644 homeassistant/components/heos/.translations/en.json create mode 100644 homeassistant/components/heos/config_flow.py create mode 100644 homeassistant/components/heos/const.py create mode 100644 homeassistant/components/heos/strings.json create mode 100644 tests/components/heos/__init__.py create mode 100644 tests/components/heos/conftest.py create mode 100644 tests/components/heos/test_init.py create mode 100644 tests/components/heos/test_media_player.py diff --git a/.coveragerc b/.coveragerc index 1cf32519adb23d..3cba85193140e5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -240,7 +240,6 @@ omit = homeassistant/components/hikvisioncam/switch.py homeassistant/components/hipchat/notify.py homeassistant/components/hitron_coda/device_tracker.py - homeassistant/components/heos/* homeassistant/components/hive/* homeassistant/components/hlk_sw16/* homeassistant/components/homekit_controller/* diff --git a/CODEOWNERS b/CODEOWNERS index e9d7a652a66600..9abf1396c6159e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -131,6 +131,7 @@ homeassistant/components/gtfs/sensor.py @robbiet480 # H homeassistant/components/harmony/* @ehendrix23 +homeassistant/components/heos/* @andrewsayre homeassistant/components/hikvision/binary_sensor.py @mezz64 homeassistant/components/history_graph/* @andrey-git homeassistant/components/hive/* @Rendili @KJonline diff --git a/homeassistant/components/heos/.translations/en.json b/homeassistant/components/heos/.translations/en.json new file mode 100644 index 00000000000000..de440ec611aeda --- /dev/null +++ b/homeassistant/components/heos/.translations/en.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index e9b775b05d0834..536b4f8623b565 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -1,5 +1,4 @@ """Denon HEOS Media Player.""" - import asyncio import logging @@ -7,13 +6,15 @@ from homeassistant.components.media_player.const import ( DOMAIN as MEDIA_PLAYER_DOMAIN) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.typing import ConfigType, HomeAssistantType -DOMAIN = 'heos' -REQUIREMENTS = ['aioheos==0.4.0'] +from .config_flow import format_title +from .const import DATA_CONTROLLER, DOMAIN + +REQUIREMENTS = ['pyheos==0.2.0'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -26,27 +27,66 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the HEOS component.""" - from aioheos import AioHeosController - host = config[DOMAIN][CONF_HOST] - controller = AioHeosController(hass.loop, host) + entries = hass.config_entries.async_entries(DOMAIN) + if not entries: + # Create new entry based on config + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={'source': 'import'}, + data={CONF_HOST: host})) + else: + # Check if host needs to be updated + entry = entries[0] + if entry.data[CONF_HOST] != host: + entry.data[CONF_HOST] = host + entry.title = format_title(host) + hass.config_entries.async_update_entry(entry) + + return True + +async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): + """Initialize config entry which represents the HEOS controller.""" + from pyheos import Heos + host = entry.data[CONF_HOST] + # Setting all_progress_events=False ensures that we only receive a + # media position update upon start of playback or when media changes + controller = Heos(host, all_progress_events=False) try: - await asyncio.wait_for(controller.connect(), timeout=5.0) - except asyncio.TimeoutError: - _LOGGER.error('Timeout during setup.') + await controller.connect(auto_reconnect=True) + # Auto reconnect only operates if initial connection was successful. + except (asyncio.TimeoutError, ConnectionError) as error: + await controller.disconnect() + _LOGGER.exception("Unable to connect to controller %s: %s", + host, type(error).__name__) return False - async def controller_close(event): - """Close connection when HASS shutsdown.""" - await controller.close() + async def disconnect_controller(event): + await controller.disconnect() + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller_close) + try: + players = await controller.get_players() + except (asyncio.TimeoutError, ConnectionError) as error: + await controller.disconnect() + _LOGGER.exception("Unable to retrieve players: %s", + type(error).__name__) + return False - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] = controller + hass.data[DOMAIN] = { + DATA_CONTROLLER: controller, + MEDIA_PLAYER_DOMAIN: players + } + hass.async_create_task(hass.config_entries.async_forward_entry_setup( + entry, MEDIA_PLAYER_DOMAIN)) + return True - hass.async_create_task(async_load_platform( - hass, MEDIA_PLAYER_DOMAIN, DOMAIN, {}, config)) - return True +async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry): + """Unload a config entry.""" + controller = hass.data[DOMAIN][DATA_CONTROLLER] + await controller.disconnect() + hass.data.pop(DOMAIN) + return await hass.config_entries.async_forward_entry_unload( + entry, MEDIA_PLAYER_DOMAIN) diff --git a/homeassistant/components/heos/config_flow.py b/homeassistant/components/heos/config_flow.py new file mode 100644 index 00000000000000..9c4cfc211ae727 --- /dev/null +++ b/homeassistant/components/heos/config_flow.py @@ -0,0 +1,25 @@ +"""Config flow to configure Heos.""" +from homeassistant import config_entries +from homeassistant.const import CONF_HOST + +from .const import DOMAIN + + +def format_title(host: str) -> str: + """Format the title for config entries.""" + return "Controller ({})".format(host) + + +@config_entries.HANDLERS.register(DOMAIN) +class HeosFlowHandler(config_entries.ConfigFlow): + """Define a flow for HEOS.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + + async def async_step_import(self, user_input=None): + """Occurs when an entry is setup through config.""" + host = user_input[CONF_HOST] + return self.async_create_entry( + title=format_title(host), + data={CONF_HOST: host}) diff --git a/homeassistant/components/heos/const.py b/homeassistant/components/heos/const.py new file mode 100644 index 00000000000000..65c452e4a71f03 --- /dev/null +++ b/homeassistant/components/heos/const.py @@ -0,0 +1,4 @@ +"""Const for the HEOS integration.""" + +DATA_CONTROLLER = "controller" +DOMAIN = 'heos' diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 8047ffd0775c6e..f96435dc713541 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -1,42 +1,35 @@ """Denon HEOS Media Player.""" +from functools import reduce +from operator import ior from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( - DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, - SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_STOP, - SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) + DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_SHUFFLE_SET, + SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING +from homeassistant.util.dt import utcnow -from . import DOMAIN as HEOS_DOMAIN +from .const import DOMAIN as HEOS_DOMAIN -DEPENDENCIES = ["heos"] +DEPENDENCIES = ['heos'] -SUPPORT_HEOS = ( - SUPPORT_PLAY - | SUPPORT_STOP - | SUPPORT_PAUSE - | SUPPORT_PLAY_MEDIA - | SUPPORT_PREVIOUS_TRACK - | SUPPORT_NEXT_TRACK - | SUPPORT_VOLUME_MUTE - | SUPPORT_VOLUME_SET - | SUPPORT_VOLUME_STEP -) +BASE_SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ + SUPPORT_VOLUME_STEP | SUPPORT_CLEAR_PLAYLIST | \ + SUPPORT_SHUFFLE_SET -PLAY_STATE_TO_STATE = { - "play": STATE_PLAYING, - "pause": STATE_PAUSED, - "stop": STATE_IDLE, -} +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Platform uses config entry setup.""" + pass -async def async_setup_platform(hass, config, async_add_devices, - discover_info=None): - """Set up the HEOS platform.""" - controller = hass.data[HEOS_DOMAIN][DOMAIN] - players = controller.get_players() - devices = [HeosMediaPlayer(p) for p in players] - async_add_devices(devices, True) + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Add binary sensors for a config entry.""" + players = hass.data[HEOS_DOMAIN][DOMAIN] + devices = [HeosMediaPlayer(player) for player in players.values()] + async_add_entities(devices, True) class HeosMediaPlayer(MediaPlayerDevice): @@ -44,109 +37,225 @@ class HeosMediaPlayer(MediaPlayerDevice): def __init__(self, player): """Initialize.""" + from pyheos import const + self._media_position_updated_at = None self._player = player + self._signals = [] + self._supported_features = BASE_SUPPORTED_FEATURES + self._play_state_to_state = { + const.PLAY_STATE_PLAY: STATE_PLAYING, + const.PLAY_STATE_STOP: STATE_IDLE, + const.PLAY_STATE_PAUSE: STATE_PAUSED + } + self._control_to_support = { + const.CONTROL_PLAY: SUPPORT_PLAY, + const.CONTROL_PAUSE: SUPPORT_PAUSE, + const.CONTROL_STOP: SUPPORT_STOP, + const.CONTROL_PLAY_PREVIOUS: SUPPORT_PREVIOUS_TRACK, + const.CONTROL_PLAY_NEXT: SUPPORT_NEXT_TRACK + } + + async def _controller_event(self, event): + """Handle controller event.""" + from pyheos import const + if event == const.EVENT_PLAYERS_CHANGED: + await self.async_update_ha_state(True) + + async def _heos_event(self, event): + """Handle connection event.""" + await self.async_update_ha_state(True) + + async def _player_update(self, player_id, event): + """Handle player attribute updated.""" + from pyheos import const + if self._player.player_id != player_id: + return + if event == const.EVENT_PLAYER_NOW_PLAYING_PROGRESS: + self._media_position_updated_at = utcnow() + await self.async_update_ha_state(True) + + async def async_added_to_hass(self): + """Device added to hass.""" + from pyheos import const + # Update state when attributes of the player change + self._signals.append(self._player.heos.dispatcher.connect( + const.SIGNAL_PLAYER_EVENT, self._player_update)) + # Update state when available players change + self._signals.append(self._player.heos.dispatcher.connect( + const.SIGNAL_CONTROLLER_EVENT, self._controller_event)) + # Update state upon connect/disconnects + self._signals.append(self._player.heos.dispatcher.connect( + const.SIGNAL_HEOS_EVENT, self._heos_event)) + + async def async_clear_playlist(self): + """Clear players playlist.""" + await self._player.clear_queue() + + async def async_media_pause(self): + """Send pause command.""" + await self._player.pause() + + async def async_media_play(self): + """Send play command.""" + await self._player.play() + + async def async_media_previous_track(self): + """Send previous track command.""" + await self._player.play_previous() + + async def async_media_next_track(self): + """Send next track command.""" + await self._player.play_next() - def _update_state(self): - self.async_schedule_update_ha_state() + async def async_media_stop(self): + """Send stop command.""" + await self._player.stop() + + async def async_mute_volume(self, mute): + """Mute the volume.""" + await self._player.set_mute(mute) + + async def async_set_shuffle(self, shuffle): + """Enable/disable shuffle mode.""" + await self._player.set_play_mode(self._player.repeat, shuffle) + + async def async_set_volume_level(self, volume): + """Set volume level, range 0..1.""" + await self._player.set_volume(volume * 100) async def async_update(self): - """Update the player.""" - self._player.request_update() + """Update supported features of the player.""" + controls = self._player.now_playing_media.supported_controls + current_support = [self._control_to_support[control] + for control in controls] + self._supported_features = reduce(ior, current_support, + BASE_SUPPORTED_FEATURES) + + async def async_will_remove_from_hass(self): + """Disconnect the device when removed.""" + for signal_remove in self._signals: + signal_remove() + self._signals.clear() - async def async_added_to_hass(self): - """Device added to hass.""" - self._player.state_change_callback = self._update_state + @property + def available(self) -> bool: + """Return True if the device is available.""" + return self._player.available @property - def unique_id(self): - """Get unique id of the player.""" - return self._player.player_id + def device_info(self) -> dict: + """Get attributes about the device.""" + return { + 'identifiers': { + (DOMAIN, self._player.player_id) + }, + 'name': self._player.name, + 'model': self._player.model, + 'manufacturer': 'HEOS', + 'sw_version': self._player.version + } @property - def name(self): - """Return the name of the device.""" - return self._player.name + def device_state_attributes(self) -> dict: + """Get additional attribute about the state.""" + return { + 'media_album_id': self._player.now_playing_media.album_id, + 'media_queue_id': self._player.now_playing_media.queue_id, + 'media_source_id': self._player.now_playing_media.source_id, + 'media_station': self._player.now_playing_media.station, + 'media_type': self._player.now_playing_media.type + } @property - def volume_level(self): - """Volume level of the device (0..1).""" - volume = self._player.volume - return float(volume) / 100 + def is_volume_muted(self) -> bool: + """Boolean if volume is currently muted.""" + return self._player.is_muted @property - def state(self): - """Get state.""" - return PLAY_STATE_TO_STATE.get(self._player.play_state) + def media_album_name(self) -> str: + """Album name of current playing media, music track only.""" + return self._player.now_playing_media.album @property - def should_poll(self): - """No polling needed.""" - return False + def media_artist(self) -> str: + """Artist of current playing media, music track only.""" + return self._player.now_playing_media.artist + + @property + def media_content_id(self) -> str: + """Content ID of current playing media.""" + return self._player.now_playing_media.media_id @property - def media_content_type(self): + def media_content_type(self) -> str: """Content type of current playing media.""" return MEDIA_TYPE_MUSIC @property - def media_artist(self): - """Artist of current playing media.""" - return self._player.media_artist + def media_duration(self): + """Duration of current playing media in seconds.""" + duration = self._player.now_playing_media.duration + if isinstance(duration, int): + return duration / 1000 + return None @property - def media_title(self): - """Album name of current playing media.""" - return self._player.media_title + def media_position(self): + """Position of current playing media in seconds.""" + # Some media doesn't have duration but reports position, return None + if not self._player.now_playing_media.duration: + return None + return self._player.now_playing_media.current_position / 1000 @property - def media_album_name(self): - """Album name of current playing media.""" - return self._player.media_album + def media_position_updated_at(self): + """When was the position of the current playing media valid.""" + # Some media doesn't have duration but reports position, return None + if not self._player.now_playing_media.duration: + return None + return self._media_position_updated_at @property - def media_image_url(self): - """Return the image url of current playing media.""" - return self._player.media_image_url + def media_image_url(self) -> str: + """Image url of current playing media.""" + return self._player.now_playing_media.image_url @property - def media_content_id(self): - """Return the content ID of current playing media.""" - return self._player.media_id + def media_title(self) -> str: + """Title of current playing media.""" + return self._player.now_playing_media.song @property - def is_volume_muted(self): - """Boolean if volume is currently muted.""" - return self._player.mute == "on" - - async def async_mute_volume(self, mute): - """Mute volume.""" - self._player.set_mute(mute) - - async def async_media_next_track(self): - """Go TO next track.""" - self._player.play_next() + def name(self) -> str: + """Return the name of the device.""" + return self._player.name - async def async_media_previous_track(self): - """Go TO previous track.""" - self._player.play_previous() + @property + def should_poll(self) -> bool: + """No polling needed for this device.""" + return False @property - def supported_features(self): - """Flag of media commands that are supported.""" - return SUPPORT_HEOS + def shuffle(self) -> bool: + """Boolean if shuffle is enabled.""" + return self._player.shuffle - async def async_set_volume_level(self, volume): - """Set volume level, range 0..1.""" - self._player.set_volume(volume * 100) + @property + def state(self) -> str: + """State of the player.""" + return self._play_state_to_state[self._player.state] - async def async_media_play(self): - """Play media player.""" - self._player.play() + @property + def supported_features(self) -> int: + """Flag media player features that are supported.""" + return self._supported_features - async def async_media_stop(self): - """Stop media player.""" - self._player.stop() + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return str(self._player.player_id) - async def async_media_pause(self): - """Pause media player.""" - self._player.pause() + @property + def volume_level(self) -> float: + """Volume level of the media player (0..1).""" + return self._player.volume / 100 diff --git a/homeassistant/components/heos/strings.json b/homeassistant/components/heos/strings.json new file mode 100644 index 00000000000000..de440ec611aeda --- /dev/null +++ b/homeassistant/components/heos/strings.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Heos" + } +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index aeb88da5b7abeb..f105a8a711cb7b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -123,9 +123,6 @@ aioftp==0.12.0 # homeassistant.components.harmony.remote aioharmony==0.1.8 -# homeassistant.components.heos -aioheos==0.4.0 - # homeassistant.components.emulated_hue # homeassistant.components.http aiohttp_cors==0.7.0 @@ -1076,6 +1073,9 @@ pygtt==1.1.2 # homeassistant.components.version.sensor pyhaversion==2.0.3 +# homeassistant.components.heos +pyheos==0.2.0 + # homeassistant.components.hikvision.binary_sensor pyhik==0.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 45a51ec6dcbcfc..6c7f5b6a5a7b55 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -205,6 +205,9 @@ pydeconz==54 # homeassistant.components.zwave pydispatcher==2.0.5 +# homeassistant.components.heos +pyheos==0.2.0 + # homeassistant.components.homematic pyhomematic==0.1.58 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index d2d6588672ff13..5cc347249f793b 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -90,6 +90,7 @@ 'pyblackbird', 'pydeconz', 'pydispatcher', + 'pyheos', 'pyhomematic', 'pylitejet', 'pymonoprice', diff --git a/tests/components/heos/__init__.py b/tests/components/heos/__init__.py new file mode 100644 index 00000000000000..3a774529c69792 --- /dev/null +++ b/tests/components/heos/__init__.py @@ -0,0 +1 @@ +"""Tests for the Heos component.""" diff --git a/tests/components/heos/conftest.py b/tests/components/heos/conftest.py new file mode 100644 index 00000000000000..6aa2f316088a6b --- /dev/null +++ b/tests/components/heos/conftest.py @@ -0,0 +1,67 @@ +"""Configuration for HEOS tests.""" +from asynctest.mock import Mock, patch as patch +from pyheos import Dispatcher, HeosPlayer, const +import pytest + +from homeassistant.components.heos import DOMAIN +from homeassistant.const import CONF_HOST + +from tests.common import MockConfigEntry + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(): + """Create a mock HEOS config entry.""" + return MockConfigEntry(domain=DOMAIN, data={CONF_HOST: '127.0.0.1'}, + title='Controller (127.0.0.1)') + + +@pytest.fixture(name="controller") +def controller_fixture(players): + """Create a mock Heos controller fixture.""" + with patch("pyheos.Heos", autospec=True) as mock: + mock_heos = mock.return_value + mock_heos.get_players.return_value = players + mock_heos.players = players + yield mock_heos + + +@pytest.fixture(name="config") +def config_fixture(): + """Create hass config fixture.""" + return { + DOMAIN: {CONF_HOST: '127.0.0.1'} + } + + +@pytest.fixture(name="players") +def player_fixture(): + """Create a mock HeosPlayer.""" + player = Mock(HeosPlayer, autospec=True) + player.heos.dispatcher = Dispatcher() + player.player_id = 1 + player.name = "Test Player" + player.model = "Test Model" + player.version = "1.0.0" + player.is_muted = False + player.available = True + player.state = const.PLAY_STATE_STOP + player.ip_address = "127.0.0.1" + player.network = "wired" + player.shuffle = False + player.repeat = const.REPEAT_OFF + player.volume = 25 + player.now_playing_media.supported_controls = const.CONTROLS_ALL + player.now_playing_media.album_id = 1 + player.now_playing_media.queue_id = 1 + player.now_playing_media.source_id = 1 + player.now_playing_media.station = "Station Name" + player.now_playing_media.type = "Station" + player.now_playing_media.album = "Album" + player.now_playing_media.artist = "Artist" + player.now_playing_media.media_id = "1" + player.now_playing_media.duration = None + player.now_playing_media.current_position = None + player.now_playing_media.image_url = "http://" + player.now_playing_media.song = "Song" + return {player.player_id: player} diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py new file mode 100644 index 00000000000000..d1932da5abb9ba --- /dev/null +++ b/tests/components/heos/test_init.py @@ -0,0 +1,104 @@ +"""Tests for the init module.""" +import asyncio + +from asynctest import patch + +from homeassistant.components.heos import async_setup_entry, async_unload_entry +from homeassistant.components.heos.const import DATA_CONTROLLER, DOMAIN +from homeassistant.components.media_player.const import ( + DOMAIN as MEDIA_PLAYER_DOMAIN) +from homeassistant.const import CONF_HOST +from homeassistant.setup import async_setup_component + + +async def test_async_setup_creates_entry(hass, config): + """Test component setup creates entry from config.""" + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + entry = entries[0] + assert entry.title == 'Controller (127.0.0.1)' + assert entry.data == {CONF_HOST: '127.0.0.1'} + + +async def test_async_setup_updates_entry(hass, config_entry, config): + """Test component setup updates entry from config.""" + config[DOMAIN][CONF_HOST] = '127.0.0.2' + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + entry = entries[0] + assert entry.title == 'Controller (127.0.0.2)' + assert entry.data == {CONF_HOST: '127.0.0.2'} + + +async def test_async_setup_returns_true(hass, config_entry, config): + """Test component setup updates entry from config.""" + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0] == config_entry + + +async def test_async_setup_entry_loads_platforms( + hass, config_entry, controller): + """Test load connects to heos, retrieves players, and loads platforms.""" + config_entry.add_to_hass(hass) + with patch.object( + hass.config_entries, 'async_forward_entry_setup') as forward_mock: + assert await async_setup_entry(hass, config_entry) + # Assert platforms loaded + await hass.async_block_till_done() + assert forward_mock.call_count == 1 + assert controller.connect.call_count == 1 + controller.disconnect.assert_not_called() + assert hass.data[DOMAIN] == { + DATA_CONTROLLER: controller, + MEDIA_PLAYER_DOMAIN: controller.players + } + + +async def test_async_setup_entry_connect_failure( + hass, config_entry, controller): + """Test failure to connect does not load entry.""" + config_entry.add_to_hass(hass) + errors = [ConnectionError, asyncio.TimeoutError] + for error in errors: + controller.connect.side_effect = error + assert not await async_setup_entry(hass, config_entry) + await hass.async_block_till_done() + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 + controller.connect.reset_mock() + controller.disconnect.reset_mock() + + +async def test_async_setup_entry_player_failure( + hass, config_entry, controller): + """Test failure to retrieve players does not load entry.""" + config_entry.add_to_hass(hass) + errors = [ConnectionError, asyncio.TimeoutError] + for error in errors: + controller.get_players.side_effect = error + assert not await async_setup_entry(hass, config_entry) + await hass.async_block_till_done() + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 + controller.connect.reset_mock() + controller.disconnect.reset_mock() + + +async def test_unload_entry(hass, config_entry, controller): + """Test entries are unloaded correctly.""" + hass.data[DOMAIN] = {DATA_CONTROLLER: controller} + with patch.object(hass.config_entries, 'async_forward_entry_unload', + return_value=True) as unload: + assert await async_unload_entry(hass, config_entry) + await hass.async_block_till_done() + assert controller.disconnect.call_count == 1 + assert unload.call_count == 1 diff --git a/tests/components/heos/test_media_player.py b/tests/components/heos/test_media_player.py new file mode 100644 index 00000000000000..d065740f7c9926 --- /dev/null +++ b/tests/components/heos/test_media_player.py @@ -0,0 +1,180 @@ +"""Tests for the Heos Media Player platform.""" +from pyheos import const + +from homeassistant.components.heos import media_player +from homeassistant.components.heos.const import DOMAIN +from homeassistant.components.media_player.const import ( + ATTR_MEDIA_ALBUM_NAME, ATTR_MEDIA_ARTIST, ATTR_MEDIA_CONTENT_ID, + ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_POSITION, + ATTR_MEDIA_POSITION_UPDATED_AT, ATTR_MEDIA_SHUFFLE, ATTR_MEDIA_TITLE, + ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, + DOMAIN as MEDIA_PLAYER_DOMAIN, MEDIA_TYPE_MUSIC, SERVICE_CLEAR_PLAYLIST, + SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, + SUPPORT_STOP) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_SHUFFLE_SET, + SERVICE_VOLUME_MUTE, SERVICE_VOLUME_SET, STATE_IDLE, STATE_PLAYING, + STATE_UNAVAILABLE) +from homeassistant.setup import async_setup_component + + +async def setup_platform(hass, config_entry, config): + """Set up the media player platform for testing.""" + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + +async def test_async_setup_platform(): + """Test setup platform does nothing (it uses config entries).""" + await media_player.async_setup_platform(None, None, None) + + +async def test_state_attributes(hass, config_entry, config, controller): + """Tests the state attributes.""" + await setup_platform(hass, config_entry, config) + state = hass.states.get('media_player.test_player') + assert state.state == STATE_IDLE + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.25 + assert not state.attributes[ATTR_MEDIA_VOLUME_MUTED] + assert state.attributes[ATTR_MEDIA_CONTENT_ID] == "1" + assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC + assert ATTR_MEDIA_DURATION not in state.attributes + assert ATTR_MEDIA_POSITION not in state.attributes + assert state.attributes[ATTR_MEDIA_TITLE] == "Song" + assert state.attributes[ATTR_MEDIA_ARTIST] == "Artist" + assert state.attributes[ATTR_MEDIA_ALBUM_NAME] == "Album" + assert not state.attributes[ATTR_MEDIA_SHUFFLE] + assert state.attributes['media_album_id'] == 1 + assert state.attributes['media_queue_id'] == 1 + assert state.attributes['media_source_id'] == 1 + assert state.attributes['media_station'] == "Station Name" + assert state.attributes['media_type'] == "Station" + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Player" + assert state.attributes[ATTR_SUPPORTED_FEATURES] == \ + SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_STOP | SUPPORT_NEXT_TRACK | \ + SUPPORT_PREVIOUS_TRACK | media_player.BASE_SUPPORTED_FEATURES + + +async def test_updates_start_from_signals( + hass, config_entry, config, controller): + """Tests dispatched signals update player.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + + # Test player does not update for other players + player.state = const.PLAY_STATE_PLAY + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, 2, + const.EVENT_PLAYER_STATE_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.state == STATE_IDLE + + # Test player_update standard events + player.state = const.PLAY_STATE_PLAY + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, player.player_id, + const.EVENT_PLAYER_STATE_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.state == STATE_PLAYING + + # Test player_update progress events + player.now_playing_media.duration = 360000 + player.now_playing_media.current_position = 1000 + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, player.player_id, + const.EVENT_PLAYER_NOW_PLAYING_PROGRESS) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_MEDIA_POSITION_UPDATED_AT] is not None + assert state.attributes[ATTR_MEDIA_DURATION] == 360 + assert state.attributes[ATTR_MEDIA_POSITION] == 1 + + # Test controller player change updates + player.available = False + player.heos.dispatcher.send( + const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.state == STATE_UNAVAILABLE + + # Test heos events update + player.available = True + player.heos.dispatcher.send( + const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.state == STATE_PLAYING + + +async def test_services(hass, config_entry, config, controller): + """Tests player commands.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_CLEAR_PLAYLIST, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.clear_queue.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_MEDIA_PAUSE, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.pause.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_MEDIA_PLAY, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.play.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.play_previous.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_MEDIA_NEXT_TRACK, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.play_next.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_MEDIA_STOP, + {ATTR_ENTITY_ID: 'media_player.test_player'}, blocking=True) + assert player.stop.call_count == 1 + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_VOLUME_MUTE, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_MEDIA_VOLUME_MUTED: True}, blocking=True) + player.set_mute.assert_called_once_with(True) + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_SHUFFLE_SET, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_MEDIA_SHUFFLE: True}, blocking=True) + player.set_play_mode.assert_called_once_with(player.repeat, True) + + player.reset_mock() + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_VOLUME_SET, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_MEDIA_VOLUME_LEVEL: 1}, blocking=True) + player.set_volume.assert_called_once_with(100) + + +async def test_unload_config_entry(hass, config_entry, config, controller): + """Test the player is removed when the config entry is unloaded.""" + await setup_platform(hass, config_entry, config) + await config_entry.async_unload(hass) + assert not hass.states.get('media_player.test_player') From 4b9e3258dcafcce5f55f8179965ee1bfbf683249 Mon Sep 17 00:00:00 2001 From: Kevin Cooper Date: Sat, 30 Mar 2019 06:36:10 +0000 Subject: [PATCH 279/605] Add command_template and value_template for MQTT alarm (#21438) * Option to send pin code with the MQTT payload for MQTT alarm * publish code via json Add publish code via json add code_disarm_required * publish code via json Add publish code via json add code_disarm_required * implemented command_template * Fix issue with night arm and add template test * implemented value_template for mqtt alarm * Fixed merge errors * Requested changes * Resolve lint errors * Resolve hound issues * Fix test formatting --- .../components/mqtt/alarm_control_panel.py | 64 +++++++++------ .../mqtt/test_alarm_control_panel.py | 82 ++++++++++++++++++- 2 files changed, 120 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 9498b59759097d..f1142baa37aac6 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -12,9 +12,9 @@ from homeassistant.components import mqtt import homeassistant.components.alarm_control_panel as alarm from homeassistant.const import ( - CONF_CODE, CONF_DEVICE, CONF_NAME, STATE_ALARM_ARMED_AWAY, - STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, - STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED) + CONF_CODE, CONF_DEVICE, CONF_NAME, CONF_VALUE_TEMPLATE, + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED) from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -29,11 +29,14 @@ _LOGGER = logging.getLogger(__name__) CONF_CODE_ARM_REQUIRED = 'code_arm_required' +CONF_CODE_DISARM_REQUIRED = 'code_disarm_required' CONF_PAYLOAD_DISARM = 'payload_disarm' CONF_PAYLOAD_ARM_HOME = 'payload_arm_home' CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away' CONF_PAYLOAD_ARM_NIGHT = 'payload_arm_night' +CONF_COMMAND_TEMPLATE = 'command_template' +DEFAULT_COMMAND_TEMPLATE = '{{action}}' DEFAULT_ARM_NIGHT = 'ARM_NIGHT' DEFAULT_ARM_AWAY = 'ARM_AWAY' DEFAULT_ARM_HOME = 'ARM_HOME' @@ -51,9 +54,13 @@ vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, + vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, + vol.Optional(CONF_CODE_DISARM_REQUIRED, default=True): cv.boolean, + vol.Optional(CONF_COMMAND_TEMPLATE, + default=DEFAULT_COMMAND_TEMPLATE): cv.template, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, - vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -125,10 +132,21 @@ async def discovery_update(self, discovery_payload): async def _subscribe_topics(self): """(Re)Subscribe to topics.""" + value_template = self._config.get(CONF_VALUE_TEMPLATE) + if value_template is not None: + value_template.hass = self.hass + command_template = self._config.get(CONF_COMMAND_TEMPLATE) + if command_template is not None: + command_template.hass = self.hass + @callback def message_received(msg): """Run when new MQTT message has been received.""" - if msg.payload not in ( + payload = msg.payload + if value_template is not None: + payload = value_template.async_render_with_possible_json_value( + msg.payload, self._state) + if payload not in ( STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_NIGHT, @@ -136,7 +154,7 @@ def message_received(msg): STATE_ALARM_TRIGGERED): _LOGGER.warning("Received unexpected payload: %s", msg.payload) return - self._state = msg.payload + self._state = payload self.async_write_ha_state() self._sub_state = await subscription.async_subscribe_topics( @@ -187,13 +205,11 @@ async def async_alarm_disarm(self, code=None): This method is a coroutine. """ - if not self._validate_code(code, 'disarming'): + code_required = self._config.get(CONF_CODE_DISARM_REQUIRED) + if code_required and not self._validate_code(code, 'disarming'): return - mqtt.async_publish( - self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_DISARM), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + payload = self._config.get(CONF_PAYLOAD_DISARM) + self._publish(code, payload) async def async_alarm_arm_home(self, code=None): """Send arm home command. @@ -203,11 +219,8 @@ async def async_alarm_arm_home(self, code=None): code_required = self._config.get(CONF_CODE_ARM_REQUIRED) if code_required and not self._validate_code(code, 'arming home'): return - mqtt.async_publish( - self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_ARM_HOME), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + action = self._config.get(CONF_PAYLOAD_ARM_HOME) + self._publish(code, action) async def async_alarm_arm_away(self, code=None): """Send arm away command. @@ -217,11 +230,8 @@ async def async_alarm_arm_away(self, code=None): code_required = self._config.get(CONF_CODE_ARM_REQUIRED) if code_required and not self._validate_code(code, 'arming away'): return - mqtt.async_publish( - self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_ARM_AWAY), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + action = self._config.get(CONF_PAYLOAD_ARM_AWAY) + self._publish(code, action) async def async_alarm_arm_night(self, code=None): """Send arm night command. @@ -231,9 +241,17 @@ async def async_alarm_arm_night(self, code=None): code_required = self._config.get(CONF_CODE_ARM_REQUIRED) if code_required and not self._validate_code(code, 'arming night'): return + action = self._config.get(CONF_PAYLOAD_ARM_NIGHT) + self._publish(code, action) + + def _publish(self, code, action): + """Publish via mqtt.""" + command_template = self._config.get(CONF_COMMAND_TEMPLATE) + values = {'action': action, 'code': code} + payload = command_template.async_render(**values) mqtt.async_publish( self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_ARM_NIGHT), + payload, self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index 742aafba8dcfda..6efaedd270bab7 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -288,15 +288,64 @@ def test_disarm_publishes_mqtt(self): self.mock_publish.async_publish.assert_called_once_with( 'alarm/command', 'DISARM', 0, False) - def test_disarm_not_publishes_mqtt_with_invalid_code(self): - """Test not publishing of MQTT messages with invalid code.""" + def test_disarm_publishes_mqtt_with_template(self): + """Test publishing of MQTT messages while disarmed. + + When command_template set to output json + """ + assert setup_component(self.hass, alarm_control_panel.DOMAIN, { + alarm_control_panel.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'alarm/state', + 'command_topic': 'alarm/command', + 'code': '1234', + 'command_template': '{\"action\":\"{{ action }}\",' + '\"code\":\"{{ code }}\"}', + } + }) + + common.alarm_disarm(self.hass, 1234) + self.hass.block_till_done() + self.mock_publish.async_publish.assert_called_once_with( + 'alarm/command', '{\"action\":\"DISARM\",\"code\":\"1234\"}', + 0, + False) + + def test_disarm_publishes_mqtt_when_code_not_req(self): + """Test publishing of MQTT messages while disarmed. + + When code_disarm_required = False + """ + assert setup_component(self.hass, alarm_control_panel.DOMAIN, { + alarm_control_panel.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'alarm/state', + 'command_topic': 'alarm/command', + 'code': '1234', + 'code_disarm_required': False + } + }) + + common.alarm_disarm(self.hass) + self.hass.block_till_done() + self.mock_publish.async_publish.assert_called_once_with( + 'alarm/command', 'DISARM', 0, False) + + def test_disarm_not_publishes_mqtt_with_invalid_code_when_req(self): + """Test not publishing of MQTT messages with invalid code. + + When code_disarm_required = True + """ assert setup_component(self.hass, alarm_control_panel.DOMAIN, { alarm_control_panel.DOMAIN: { 'platform': 'mqtt', 'name': 'test', 'state_topic': 'alarm/state', 'command_topic': 'alarm/command', - 'code': '1234' + 'code': '1234', + 'code_disarm_required': True } }) @@ -373,6 +422,33 @@ async def test_setting_attribute_via_mqtt_json_message(hass, mqtt_mock): assert '100' == state.attributes.get('val') +async def test_update_state_via_state_topic_template(hass, mqtt_mock): + """Test updating with template_value via state topic.""" + assert await async_setup_component(hass, alarm_control_panel.DOMAIN, { + alarm_control_panel.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'command_topic': 'test-topic', + 'state_topic': 'test-topic', + 'value_template': '\ + {% if (value | int) == 100 %}\ + armed_away\ + {% else %}\ + disarmed\ + {% endif %}' + } + }) + + state = hass.states.get('alarm_control_panel.test') + assert STATE_UNKNOWN == state.state + + async_fire_mqtt_message(hass, 'test-topic', '100') + await hass.async_block_till_done() + + state = hass.states.get('alarm_control_panel.test') + assert STATE_ALARM_ARMED_AWAY == state.state + + async def test_update_with_json_attrs_not_dict(hass, mqtt_mock, caplog): """Test attributes get extracted from a JSON result.""" assert await async_setup_component(hass, alarm_control_panel.DOMAIN, { From 1a39fb4de789fee375505f3bd83b9546879fd4de Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sat, 30 Mar 2019 10:09:36 +0100 Subject: [PATCH 280/605] Add table with netgear_lte sensor units (#22508) --- .../components/netgear_lte/sensor.py | 27 ++++++++----------- .../components/netgear_lte/sensor_types.py | 7 ++++- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 1be960edfe3ad9..42b0ddfa054806 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -9,7 +9,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import CONF_MONITORED_CONDITIONS, DATA_KEY, DISPATCHER_NETGEAR_LTE -from .sensor_types import SENSOR_SMS, SENSOR_USAGE +from .sensor_types import SENSOR_SMS, SENSOR_USAGE, SENSOR_UNITS DEPENDENCIES = ['netgear_lte'] @@ -79,14 +79,19 @@ def unique_id(self): """Return a unique ID like 'usage_5TG365AB0078V'.""" return self._unique_id - -class SMSSensor(LTESensor): - """Unread SMS sensor entity.""" - @property def name(self): """Return the name of the sensor.""" - return "Netgear LTE SMS" + return "Netgear LTE {}".format(self.sensor_type) + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return SENSOR_UNITS[self.sensor_type] + + +class SMSSensor(LTESensor): + """Unread SMS sensor entity.""" @property def state(self): @@ -97,16 +102,6 @@ def state(self): class UsageSensor(LTESensor): """Data usage sensor entity.""" - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return "MiB" - - @property - def name(self): - """Return the name of the sensor.""" - return "Netgear LTE usage" - @property def state(self): """Return the state of the sensor.""" diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py index b05ecf6074a9f8..673f929d9adae6 100644 --- a/homeassistant/components/netgear_lte/sensor_types.py +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -3,6 +3,11 @@ SENSOR_SMS = 'sms' SENSOR_USAGE = 'usage' -ALL = [SENSOR_SMS, SENSOR_USAGE] +SENSOR_UNITS = { + SENSOR_SMS: 'unread', + SENSOR_USAGE: 'MiB', +} + +ALL = list(SENSOR_UNITS) DEFAULT = [SENSOR_USAGE] From 906f0113adad019e6182fe1690b72cc78f3e6c7c Mon Sep 17 00:00:00 2001 From: Jc2k Date: Sat, 30 Mar 2019 10:21:11 +0000 Subject: [PATCH 281/605] Add more HomeKit device enumeration tests (#22194) * Test that Aqara Gateway, Ecobee 3 and Lennox E30 is correctly enumerated * Move json to fixtures directory * Move IO to executor --- tests/components/homekit_controller/common.py | 13 +- .../specific_devices/test_aqara_gateway.py | 41 + .../specific_devices/test_ecobee3.py | 43 + .../specific_devices/test_koogeek_ls1.py | 7 +- .../specific_devices/test_lennox_e30.py | 29 + .../homekit_controller/aqara_gateway.json | 488 ++++++++ .../fixtures/homekit_controller/ecobee3.json | 1036 +++++++++++++++++ .../ecobee3_no_sensors.json | 508 ++++++++ .../homekit_controller}/koogeek_ls1.json | 0 .../homekit_controller/lennox_e30.json | 196 ++++ 10 files changed, 2352 insertions(+), 9 deletions(-) create mode 100644 tests/components/homekit_controller/specific_devices/test_aqara_gateway.py create mode 100644 tests/components/homekit_controller/specific_devices/test_ecobee3.py create mode 100644 tests/components/homekit_controller/specific_devices/test_lennox_e30.py create mode 100644 tests/fixtures/homekit_controller/aqara_gateway.json create mode 100644 tests/fixtures/homekit_controller/ecobee3.json create mode 100644 tests/fixtures/homekit_controller/ecobee3_no_sensors.json rename tests/{components/homekit_controller/specific_devices => fixtures/homekit_controller}/koogeek_ls1.json (100%) create mode 100644 tests/fixtures/homekit_controller/lennox_e30.json diff --git a/tests/components/homekit_controller/common.py b/tests/components/homekit_controller/common.py index 2d659d42dfb67a..4da7bc00c85408 100644 --- a/tests/components/homekit_controller/common.py +++ b/tests/components/homekit_controller/common.py @@ -1,5 +1,6 @@ """Code to support homekit_controller tests.""" import json +import os from datetime import timedelta from unittest import mock @@ -13,7 +14,8 @@ DOMAIN, HOMEKIT_ACCESSORY_DISPATCH) from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import async_fire_time_changed, fire_service_discovered +from tests.common import ( + async_fire_time_changed, fire_service_discovered, load_fixture) class FakePairing: @@ -149,10 +151,13 @@ def add_characteristic(self, name): return char -def setup_accessories_from_file(path): +async def setup_accessories_from_file(hass, path): """Load an collection of accessory defs from JSON data.""" - with open(path, 'r') as accessories_data: - accessories_json = json.load(accessories_data) + accessories_fixture = await hass.async_add_executor_job( + load_fixture, + os.path.join('homekit_controller', path), + ) + accessories_json = json.loads(accessories_fixture) accessories = [] diff --git a/tests/components/homekit_controller/specific_devices/test_aqara_gateway.py b/tests/components/homekit_controller/specific_devices/test_aqara_gateway.py new file mode 100644 index 00000000000000..e0738d67083a1a --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_aqara_gateway.py @@ -0,0 +1,41 @@ +""" +Regression tests for Aqara Gateway V3. + +https://github.com/home-assistant/home-assistant/issues/20957 +""" + +from homeassistant.components.light import SUPPORT_BRIGHTNESS, SUPPORT_COLOR +from tests.components.homekit_controller.common import ( + setup_accessories_from_file, setup_test_accessories, Helper +) + + +async def test_aqara_gateway_setup(hass): + """Test that a Aqara Gateway can be correctly setup in HA.""" + accessories = await setup_accessories_from_file( + hass, 'aqara_gateway.json') + pairing = await setup_test_accessories(hass, accessories) + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + + # Check that the light is correctly found and set up + alarm_id = "alarm_control_panel.aqara_hub_1563" + alarm = entity_registry.async_get(alarm_id) + assert alarm.unique_id == 'homekit-0000000123456789-66304' + + alarm_helper = Helper( + hass, 'alarm_control_panel.aqara_hub_1563', pairing, accessories[0]) + alarm_state = await alarm_helper.poll_and_get_state() + assert alarm_state.attributes['friendly_name'] == 'Aqara Hub-1563' + + # Check that the light is correctly found and set up + light = entity_registry.async_get('light.aqara_hub_1563') + assert light.unique_id == 'homekit-0000000123456789-65792' + + light_helper = Helper( + hass, 'light.aqara_hub_1563', pairing, accessories[0]) + light_state = await light_helper.poll_and_get_state() + assert light_state.attributes['friendly_name'] == 'Aqara Hub-1563' + assert light_state.attributes['supported_features'] == ( + SUPPORT_BRIGHTNESS | SUPPORT_COLOR + ) diff --git a/tests/components/homekit_controller/specific_devices/test_ecobee3.py b/tests/components/homekit_controller/specific_devices/test_ecobee3.py new file mode 100644 index 00000000000000..e9452840074815 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_ecobee3.py @@ -0,0 +1,43 @@ +""" +Regression tests for Ecobee 3. + +https://github.com/home-assistant/home-assistant/issues/15336 +""" + +from homeassistant.components.climate.const import ( + SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) +from tests.components.homekit_controller.common import ( + setup_accessories_from_file, setup_test_accessories, Helper +) + + +async def test_ecobee3_setup(hass): + """Test that a Ecbobee 3 can be correctly setup in HA.""" + accessories = await setup_accessories_from_file(hass, 'ecobee3.json') + pairing = await setup_test_accessories(hass, accessories) + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + + climate = entity_registry.async_get('climate.homew') + assert climate.unique_id == 'homekit-123456789012-16' + + climate_helper = Helper(hass, 'climate.homew', pairing, accessories[0]) + climate_state = await climate_helper.poll_and_get_state() + assert climate_state.attributes['friendly_name'] == 'HomeW' + assert climate_state.attributes['supported_features'] == ( + SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE + ) + + occ1 = entity_registry.async_get('binary_sensor.kitchen') + assert occ1.unique_id == 'homekit-AB1C-56' + + occ1_helper = Helper( + hass, 'binary_sensor.kitchen', pairing, accessories[0]) + occ1_state = await occ1_helper.poll_and_get_state() + assert occ1_state.attributes['friendly_name'] == 'Kitchen' + + occ2 = entity_registry.async_get('binary_sensor.porch') + assert occ2.unique_id == 'homekit-AB2C-56' + + occ3 = entity_registry.async_get('binary_sensor.basement') + assert occ3.unique_id == 'homekit-AB3C-56' diff --git a/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py b/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py index 7b7981cf6de919..a741885c6d5706 100644 --- a/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py +++ b/tests/components/homekit_controller/specific_devices/test_koogeek_ls1.py @@ -1,6 +1,5 @@ """Make sure that existing Koogeek LS1 support isn't broken.""" -import os from datetime import timedelta from unittest import mock @@ -19,8 +18,7 @@ async def test_koogeek_ls1_setup(hass): """Test that a Koogeek LS1 can be correctly setup in HA.""" - profile_path = os.path.join(os.path.dirname(__file__), 'koogeek_ls1.json') - accessories = setup_accessories_from_file(profile_path) + accessories = await setup_accessories_from_file(hass, 'koogeek_ls1.json') pairing = await setup_test_accessories(hass, accessories) entity_registry = await hass.helpers.entity_registry.async_get_registry() @@ -50,8 +48,7 @@ async def test_recover_from_failure(hass, utcnow, failure_cls): See https://github.com/home-assistant/home-assistant/issues/18949 """ - profile_path = os.path.join(os.path.dirname(__file__), 'koogeek_ls1.json') - accessories = setup_accessories_from_file(profile_path) + accessories = await setup_accessories_from_file(hass, 'koogeek_ls1.json') pairing = await setup_test_accessories(hass, accessories) helper = Helper(hass, 'light.koogeek_ls1_20833f', pairing, accessories[0]) diff --git a/tests/components/homekit_controller/specific_devices/test_lennox_e30.py b/tests/components/homekit_controller/specific_devices/test_lennox_e30.py new file mode 100644 index 00000000000000..1869161b1f8ce1 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_lennox_e30.py @@ -0,0 +1,29 @@ +""" +Regression tests for Aqara Gateway V3. + +https://github.com/home-assistant/home-assistant/issues/20885 +""" + +from homeassistant.components.climate.const import ( + SUPPORT_TARGET_TEMPERATURE, SUPPORT_OPERATION_MODE) +from tests.components.homekit_controller.common import ( + setup_accessories_from_file, setup_test_accessories, Helper +) + + +async def test_lennox_e30_setup(hass): + """Test that a Lennox E30 can be correctly setup in HA.""" + accessories = await setup_accessories_from_file(hass, 'lennox_e30.json') + pairing = await setup_test_accessories(hass, accessories) + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + + climate = entity_registry.async_get('climate.lennox') + assert climate.unique_id == 'homekit-XXXXXXXX-100' + + climate_helper = Helper(hass, 'climate.lennox', pairing, accessories[0]) + climate_state = await climate_helper.poll_and_get_state() + assert climate_state.attributes['friendly_name'] == 'Lennox' + assert climate_state.attributes['supported_features'] == ( + SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE + ) diff --git a/tests/fixtures/homekit_controller/aqara_gateway.json b/tests/fixtures/homekit_controller/aqara_gateway.json new file mode 100644 index 00000000000000..092936f3da5089 --- /dev/null +++ b/tests/fixtures/homekit_controller/aqara_gateway.json @@ -0,0 +1,488 @@ +[ + { + "services": [ + { + "iid": 1, + "characteristics": [ + { + "value": "Aqara", + "description": "Manufacturer", + "type": "20", + "iid": 3, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "value": "ZHWA11LM", + "description": "Model", + "type": "21", + "iid": 4, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "value": "Aqara Hub-1563", + "description": "Name", + "type": "23", + "iid": 5, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "value": "0000000123456789", + "description": "Serial Number", + "type": "30", + "iid": 6, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "description": "Identify", + "iid": 7, + "perms": [ + "pw" + ], + "type": "14", + "format": "bool" + }, + { + "value": "1.4.7", + "description": "Firmware Revision", + "type": "52", + "iid": 8, + "perms": [ + "pr" + ], + "format": "string" + } + ], + "type": "3e" + }, + { + "iid": 60, + "characteristics": [ + { + "value": "1.1.0", + "description": "Protocol Version", + "type": "37", + "iid": 62, + "perms": [ + "pr" + ], + "format": "string" + } + ], + "type": "a2" + }, + { + "hidden": true, + "iid": 65536, + "characteristics": [ + { + "value": false, + "description": "New Accessory Permission", + "type": "b1c09e4c-e202-4827-b343-b0f32f727cff", + "iid": 65538, + "perms": [ + "pr", + "pw", + "ev", + "hd" + ], + "format": "bool" + }, + { + "value": "()", + "description": "Accessory Joined", + "type": "2cb22739-1e4c-4798-a712-bc2faf51afc3", + "maxLen": 256, + "iid": 65539, + "perms": [ + "pr", + "ev", + "hd" + ], + "format": "string" + }, + { + "value": " ", + "description": "Remove Accessory", + "type": "75d19fa9-218b-4943-427e-341e5d1c60cc", + "iid": 65540, + "perms": [ + "pr", + "pw", + "ev", + "hd" + ], + "format": "string" + }, + { + "maxValue": 100, + "value": 40, + "minValue": 0, + "description": "Gateway Volume", + "type": "ee56b186-b0d3-528e-8c79-c21fc9bcf437", + "unit": "percentage", + "iid": 65541, + "minStep": 1, + "perms": [ + "pr", + "pw" + ], + "format": "int" + }, + { + "value": "Chinese", + "description": "Language", + "type": "4cf1436a-755c-1277-bdb8-30be29eb8620", + "iid": 65542, + "perms": [ + "pr", + "pw" + ], + "format": "string" + }, + { + "value": "2019-02-12 06:45:07+10", + "description": "Date and Time", + "type": "4cb28907-66df-4d9c-924c-9971abf30edc", + "iid": 65543, + "perms": [ + "pr", + "pw" + ], + "format": "string" + }, + { + "value": " ", + "description": "Identify Accessory", + "type": "e1c20b22-e3a7-4b12-8ba3-c16e778648a7", + "iid": 65544, + "perms": [ + "pr", + "ev", + "hd" + ], + "format": "string" + }, + { + "value": "aiot-coap.aqara.cn", + "description": "Country Domain", + "type": "25d889cb-7135-4a21-b5b4-c1ffd6d2dd5c", + "iid": 65545, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "string" + }, + { + "value": -1, + "description": "Firmware Update Status", + "type": "7d943f6a-e052-4e96-a124-d17bf00e32cb", + "iid": 65546, + "perms": [ + "pr", + "ev", + "hd" + ], + "format": "int" + }, + { + "description": "Firmware Update Data", + "iid": 65547, + "perms": [ + "pw", + "hd" + ], + "type": "7f51dc43-dc68-4237-bae8-d705e61139f5", + "format": "data" + }, + { + "description": "Firmware Update URL", + "type": "a45efd52-0db5-4c1a-1227-513fbcd8185f", + "maxLen": 256, + "iid": 65548, + "perms": [ + "pw", + "hd" + ], + "format": "string" + }, + { + "description": "Firmware Update Checksum", + "iid": 65549, + "perms": [ + "pw", + "hd" + ], + "type": "40f0124a-579d-40e4-245e-0ef6740ea64b", + "format": "string" + } + ], + "type": "9715bf53-ab63-4449-8dc7-2485d617390a" + }, + { + "iid": 65792, + "characteristics": [ + { + "value": "Lightbulb-1563", + "description": "Name", + "type": "23", + "iid": 65794, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "value": false, + "description": "On", + "type": "25", + "iid": 65795, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "bool" + }, + { + "maxValue": 360, + "value": 0, + "minValue": 0, + "description": "Hue", + "type": "13", + "unit": "arcdegrees", + "iid": 65796, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "float" + }, + { + "maxValue": 100, + "value": 100, + "minValue": 0, + "description": "Saturation", + "type": "2f", + "unit": "percentage", + "iid": 65797, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "float" + }, + { + "maxValue": 100, + "value": 0, + "minValue": 0, + "description": "Brightness", + "type": "8", + "unit": "percentage", + "iid": 65798, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "int" + }, + { + "value": "", + "description": "Timers", + "type": "232aa6bd-6ce2-4d7f-b7cf-52305f0d2bcf", + "iid": 65799, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "tlv8" + } + ], + "type": "43" + }, + { + "iid": 66048, + "characteristics": [ + { + "value": "MIIO Service", + "description": "Name", + "type": "23", + "iid": 66050, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "value": false, + "description": "miio provisioned", + "type": "6ef066c1-08f8-46de-9121-b89b77e459e7", + "iid": 66051, + "perms": [ + "pr", + "hd" + ], + "format": "bool" + }, + { + "description": "miio bindkey", + "iid": 66052, + "perms": [ + "pw", + "hd" + ], + "type": "6ef066c2-08f8-46de-9121-b89b77e459e7", + "format": "string" + }, + { + "value": "152601563", + "description": "miio did", + "type": "6ef066c5-08f8-46de-9121-b89b77e459e7", + "iid": 66053, + "perms": [ + "pr", + "hd" + ], + "format": "string" + }, + { + "value": "lumi.gateway.aqhm01", + "description": "miio model", + "type": "6ef066c4-08f8-46de-9121-b89b77e459e7", + "iid": 66054, + "perms": [ + "pr", + "hd" + ], + "format": "string" + }, + { + "value": "ch", + "description": "miio country domain", + "type": "6ef066c3-08f8-46de-9121-b89b77e459e7", + "iid": 66055, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "string" + }, + { + "value": "country code", + "description": "miio country code", + "type": "6ef066d1-08f8-46de-9121-b89b77e459e7", + "iid": 66056, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "string" + }, + { + "value": "app", + "description": "miio config type", + "type": "6ef066d3-08f8-46de-9121-b89b77e459e7", + "iid": 66057, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "string" + }, + { + "value": 28800, + "description": "miio gmt offset", + "type": "6ef066d2-08f8-46de-9121-b89b77e459e7", + "unit": "seconds", + "iid": 66058, + "perms": [ + "pr", + "pw", + "hd" + ], + "format": "int" + } + ], + "type": "6ef066c0-08f8-46de-9121-b89b77e459e7" + }, + { + "iid": 66304, + "characteristics": [ + { + "value": "Security System", + "description": "Name", + "type": "23", + "iid": 66306, + "perms": [ + "pr" + ], + "format": "string" + }, + { + "maxValue": 4, + "value": 3, + "minValue": 0, + "description": "Security System Current State", + "type": "66", + "valid-values": [ + 1, + 3, + 4 + ], + "iid": 66307, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "format": "uint8" + }, + { + "maxValue": 3, + "value": 3, + "minValue": 0, + "description": "Security System Target State", + "type": "67", + "valid-values": [ + 1, + 3 + ], + "iid": 66308, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "uint8" + } + ], + "type": "7e" + } + ], + "aid": 1 + } +] \ No newline at end of file diff --git a/tests/fixtures/homekit_controller/ecobee3.json b/tests/fixtures/homekit_controller/ecobee3.json new file mode 100644 index 00000000000000..34c3fb4cdeab62 --- /dev/null +++ b/tests/fixtures/homekit_controller/ecobee3.json @@ -0,0 +1,1036 @@ +[ + { + "aid": 1, + "services": [ + { + "type": "3E", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 2 + }, + { + "value": "ecobee Inc.", + "perms": [ + "pr" + ], + "type": "20", + "format": "string", + "iid": 3 + }, + { + "value": "123456789012", + "perms": [ + "pr" + ], + "type": "30", + "format": "string", + "iid": 4 + }, + { + "value": "ecobee3", + "perms": [ + "pr" + ], + "type": "21", + "format": "string", + "iid": 5 + }, + { + "perms": [ + "pw" + ], + "type": "14", + "format": "bool", + "iid": 6 + }, + { + "value": "4.2.394", + "perms": [ + "pr" + ], + "type": "52", + "format": "string", + "iid": 8 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "A6", + "format": "uint32", + "iid": 9 + } + ], + "iid": 1 + }, + { + "type": "A2", + "characteristics": [ + { + "value": "1.1.0", + "perms": [ + "pr" + ], + "maxLen": 64, + "type": "37", + "format": "string", + "iid": 31 + } + ], + "iid": 30 + }, + { + "primary": true, + "type": "4A", + "characteristics": [ + { + "value": 1, + "maxValue": 2, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "F", + "minValue": 0, + "format": "uint8", + "iid": 17 + }, + { + "value": 1, + "maxValue": 3, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "33", + "minValue": 0, + "format": "uint8", + "iid": 18 + }, + { + "value": 21.8, + "maxValue": 100, + "minStep": 0.1, + "perms": [ + "pr", + "ev" + ], + "unit": "celsius", + "type": "11", + "minValue": 0, + "format": "float", + "iid": 19 + }, + { + "value": 22.2, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "35", + "minValue": 7.2, + "format": "float", + "iid": 20 + }, + { + "value": 1, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "36", + "minValue": 0, + "format": "uint8", + "iid": 21 + }, + { + "value": 24.4, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "D", + "minValue": 18.3, + "format": "float", + "iid": 22 + }, + { + "value": 22.2, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "12", + "minValue": 7.2, + "format": "float", + "iid": 23 + }, + { + "value": 34, + "maxValue": 100, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "unit": "percentage", + "type": "10", + "minValue": 0, + "format": "float", + "iid": 24 + }, + { + "value": 36, + "maxValue": 50, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "percentage", + "type": "34", + "minValue": 20, + "format": "float", + "iid": 25 + }, + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 27 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "B7DDB9A3-54BB-4572-91D2-F1F5B0510F8C", + "format": "uint8", + "iid": 33 + }, + { + "value": 22.2, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "E4489BBC-5227-4569-93E5-B345E3E5508F", + "minValue": 7.2, + "format": "float", + "iid": 34 + }, + { + "value": 24.4, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "7D381BAA-20F9-40E5-9BE9-AEB92D4BECEF", + "minValue": 18.3, + "format": "float", + "iid": 35 + }, + { + "value": 17.8, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "73AAB542-892A-4439-879A-D2A883724B69", + "minValue": 7.2, + "format": "float", + "iid": 36 + }, + { + "value": 27.8, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "5DA985F0-898A-4850-B987-B76C6C78D670", + "minValue": 18.3, + "format": "float", + "iid": 37 + }, + { + "value": 18.9, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "05B97374-6DC0-439B-A0FA-CA33F612D425", + "minValue": 7.2, + "format": "float", + "iid": 38 + }, + { + "value": 26.7, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "A251F6E7-AC46-4190-9C5D-3D06277BDF9F", + "minValue": 18.3, + "format": "float", + "iid": 39 + }, + { + "minValue": 0, + "maxValue": 3, + "minStep": 1, + "perms": [ + "pw" + ], + "type": "1B300BC2-CFFC-47FF-89F9-BD6CCF5F2853", + "format": "uint8", + "iid": 40 + }, + { + "value": "2014-01-03T00:00:00-05:00", + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "1621F556-1367-443C-AF19-82AF018E99DE", + "format": "string", + "iid": 41 + }, + { + "perms": [ + "pw" + ], + "type": "FA128DE6-9D7D-49A4-B6D8-4E4E234DEE38", + "format": "bool", + "iid": 48 + }, + { + "value": 1, + "perms": [ + "pr", + "ev" + ], + "type": "4A6AE4F6-036C-495D-87CC-B3702B437741", + "format": "uint8", + "iid": 49 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "DB7BF261-7042-4194-8BD1-3AA22830AEDD", + "format": "uint8", + "iid": 50 + }, + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "41935E3E-B54D-42E9-B8B9-D33C6319F0AF", + "format": "bool", + "iid": 51 + }, + { + "minValue": 0, + "maxValue": 100, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "C35DA3C0-E004-40E3-B153-46655CDD9214", + "value": 0, + "format": "uint8", + "iid": 52 + }, + { + "value": 100, + "perms": [ + "pr", + "ev" + ], + "type": "48F62AEC-4171-4B4A-8F0E-1EEB6708B3FB", + "format": "uint8", + "iid": 53 + }, + { + "value": "The Hive is humming along. You have no pending alerts or reminders.", + "perms": [ + "pr", + "ev" + ], + "iid": 54, + "type": "1B1515F2-CC45-409F-991F-C480987F92C3", + "format": "string", + "maxLen": 256 + } + ], + "iid": 16 + }, + { + "type": "85", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 28 + }, + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "22", + "format": "bool", + "iid": 66 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "value": 2980, + "format": "int", + "iid": 67 + } + ], + "iid": 56 + }, + { + "type": "86", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 29 + }, + { + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "71", + "value": 1, + "format": "uint8", + "iid": 65 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "A8f798E0-4A40-11E6-BDF4-0800200C9A66", + "value": 2980, + "format": "int", + "iid": 68 + } + ], + "iid": 57 + } + ] + }, + { + "aid": 2, + "services": [ + { + "type": "3E", + "characteristics": [ + { + "value": "Kitchen", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 2049 + }, + { + "value": "ecobee Inc.", + "perms": [ + "pr" + ], + "type": "20", + "format": "string", + "iid": 2050 + }, + { + "value": "AB1C", + "perms": [ + "pr" + ], + "type": "30", + "format": "string", + "iid": 2051 + }, + { + "value": "REMOTE SENSOR", + "perms": [ + "pr" + ], + "type": "21", + "format": "string", + "iid": 2052 + }, + { + "value": "1.0.0", + "perms": [ + "pr" + ], + "type": "52", + "format": "string", + "iid": 8 + }, + { + "perms": [ + "pw" + ], + "type": "14", + "format": "bool", + "iid": 2053 + } + ], + "iid": 1 + }, + { + "type": "8A", + "characteristics": [ + { + "value": 21.5, + "maxValue": 100, + "minStep": 0.1, + "perms": [ + "pr", + "ev" + ], + "unit": "celsius", + "type": "11", + "minValue": 0, + "format": "float", + "iid": 2064 + }, + { + "value": "Kitchen", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 2067 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 2066 + }, + { + "value": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "minValue": 0, + "format": "uint8", + "iid": 2065 + } + ], + "iid": 55 + }, + { + "type": "85", + "characteristics": [ + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "22", + "format": "bool", + "iid": 2060 + }, + { + "value": "Kitchen", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 2063 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 2062 + }, + { + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "value": 0, + "format": "uint8", + "iid": 2061 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "value": 3620, + "format": "int", + "iid": 2059 + } + ], + "iid": 56 + } + ] + }, + { + "aid": 3, + "services": [ + { + "type": "3E", + "characteristics": [ + { + "value": "Porch", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 3073 + }, + { + "value": "ecobee Inc.", + "perms": [ + "pr" + ], + "type": "20", + "format": "string", + "iid": 3074 + }, + { + "value": "AB2C", + "perms": [ + "pr" + ], + "type": "30", + "format": "string", + "iid": 3075 + }, + { + "value": "REMOTE SENSOR", + "perms": [ + "pr" + ], + "type": "21", + "format": "string", + "iid": 3076 + }, + { + "value": "1.0.0", + "perms": [ + "pr" + ], + "type": "52", + "format": "string", + "iid": 8 + }, + { + "perms": [ + "pw" + ], + "type": "14", + "format": "bool", + "iid": 3077 + } + ], + "iid": 1 + }, + { + "type": "8A", + "characteristics": [ + { + "value": 21, + "maxValue": 100, + "minStep": 0.1, + "perms": [ + "pr", + "ev" + ], + "unit": "celsius", + "type": "11", + "minValue": 0, + "format": "float", + "iid": 3088 + }, + { + "value": "Porch", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 3091 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 3090 + }, + { + "value": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "minValue": 0, + "format": "uint8", + "iid": 3089 + } + ], + "iid": 55 + }, + { + "type": "85", + "characteristics": [ + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "22", + "format": "bool", + "iid": 3084 + }, + { + "value": "Porch", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 3087 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 3086 + }, + { + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "value": 0, + "format": "uint8", + "iid": 3085 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "value": 5766, + "format": "int", + "iid": 3083 + } + ], + "iid": 56 + } + ] + }, + { + "aid": 4, + "services": [ + { + "type": "3E", + "characteristics": [ + { + "value": "Basement", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 4097 + }, + { + "value": "ecobee Inc.", + "perms": [ + "pr" + ], + "type": "20", + "format": "string", + "iid": 4098 + }, + { + "value": "AB3C", + "perms": [ + "pr" + ], + "type": "30", + "format": "string", + "iid": 4099 + }, + { + "value": "REMOTE SENSOR", + "perms": [ + "pr" + ], + "type": "21", + "format": "string", + "iid": 4100 + }, + { + "value": "1.0.0", + "perms": [ + "pr" + ], + "type": "52", + "format": "string", + "iid": 8 + }, + { + "perms": [ + "pw" + ], + "type": "14", + "format": "bool", + "iid": 4101 + } + ], + "iid": 1 + }, + { + "type": "8A", + "characteristics": [ + { + "value": 20.7, + "maxValue": 100, + "minStep": 0.1, + "perms": [ + "pr", + "ev" + ], + "unit": "celsius", + "type": "11", + "minValue": 0, + "format": "float", + "iid": 4112 + }, + { + "value": "Basement", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 4115 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 4114 + }, + { + "value": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "minValue": 0, + "format": "uint8", + "iid": 4113 + } + ], + "iid": 55 + }, + { + "type": "85", + "characteristics": [ + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "22", + "format": "bool", + "iid": 4108 + }, + { + "value": "Basement", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 4111 + }, + { + "value": true, + "perms": [ + "pr", + "ev" + ], + "type": "75", + "format": "bool", + "iid": 4110 + }, + { + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "79", + "value": 0, + "format": "uint8", + "iid": 4109 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "value": 5472, + "format": "int", + "iid": 4107 + } + ], + "iid": 56 + } + ] + } +] \ No newline at end of file diff --git a/tests/fixtures/homekit_controller/ecobee3_no_sensors.json b/tests/fixtures/homekit_controller/ecobee3_no_sensors.json new file mode 100644 index 00000000000000..3d3c2ebad2bb1d --- /dev/null +++ b/tests/fixtures/homekit_controller/ecobee3_no_sensors.json @@ -0,0 +1,508 @@ +[ + { + "aid": 1, + "services": [ + { + "type": "3E", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 2 + }, + { + "value": "ecobee Inc.", + "perms": [ + "pr" + ], + "type": "20", + "format": "string", + "iid": 3 + }, + { + "value": "123456789012", + "perms": [ + "pr" + ], + "type": "30", + "format": "string", + "iid": 4 + }, + { + "value": "ecobee3", + "perms": [ + "pr" + ], + "type": "21", + "format": "string", + "iid": 5 + }, + { + "perms": [ + "pw" + ], + "type": "14", + "format": "bool", + "iid": 6 + }, + { + "value": "4.2.394", + "perms": [ + "pr" + ], + "type": "52", + "format": "string", + "iid": 8 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "A6", + "format": "uint32", + "iid": 9 + } + ], + "iid": 1 + }, + { + "type": "A2", + "characteristics": [ + { + "value": "1.1.0", + "perms": [ + "pr" + ], + "maxLen": 64, + "type": "37", + "format": "string", + "iid": 31 + } + ], + "iid": 30 + }, + { + "primary": true, + "type": "4A", + "characteristics": [ + { + "value": 1, + "maxValue": 2, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "F", + "minValue": 0, + "format": "uint8", + "iid": 17 + }, + { + "value": 1, + "maxValue": 3, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "33", + "minValue": 0, + "format": "uint8", + "iid": 18 + }, + { + "value": 21.8, + "maxValue": 100, + "minStep": 0.1, + "perms": [ + "pr", + "ev" + ], + "unit": "celsius", + "type": "11", + "minValue": 0, + "format": "float", + "iid": 19 + }, + { + "value": 22.2, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "35", + "minValue": 7.2, + "format": "float", + "iid": 20 + }, + { + "value": 1, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "36", + "minValue": 0, + "format": "uint8", + "iid": 21 + }, + { + "value": 24.4, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "D", + "minValue": 18.3, + "format": "float", + "iid": 22 + }, + { + "value": 22.2, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "12", + "minValue": 7.2, + "format": "float", + "iid": 23 + }, + { + "value": 34, + "maxValue": 100, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "unit": "percentage", + "type": "10", + "minValue": 0, + "format": "float", + "iid": 24 + }, + { + "value": 36, + "maxValue": 50, + "minStep": 1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "percentage", + "type": "34", + "minValue": 20, + "format": "float", + "iid": 25 + }, + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 27 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "B7DDB9A3-54BB-4572-91D2-F1F5B0510F8C", + "format": "uint8", + "iid": 33 + }, + { + "value": 22.2, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "E4489BBC-5227-4569-93E5-B345E3E5508F", + "minValue": 7.2, + "format": "float", + "iid": 34 + }, + { + "value": 24.4, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "7D381BAA-20F9-40E5-9BE9-AEB92D4BECEF", + "minValue": 18.3, + "format": "float", + "iid": 35 + }, + { + "value": 17.8, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "73AAB542-892A-4439-879A-D2A883724B69", + "minValue": 7.2, + "format": "float", + "iid": 36 + }, + { + "value": 27.8, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "5DA985F0-898A-4850-B987-B76C6C78D670", + "minValue": 18.3, + "format": "float", + "iid": 37 + }, + { + "value": 18.9, + "maxValue": 26.1, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "05B97374-6DC0-439B-A0FA-CA33F612D425", + "minValue": 7.2, + "format": "float", + "iid": 38 + }, + { + "value": 26.7, + "maxValue": 33.3, + "minStep": 0.1, + "perms": [ + "pr", + "pw", + "ev" + ], + "unit": "celsius", + "type": "A251F6E7-AC46-4190-9C5D-3D06277BDF9F", + "minValue": 18.3, + "format": "float", + "iid": 39 + }, + { + "minValue": 0, + "maxValue": 3, + "minStep": 1, + "perms": [ + "pw" + ], + "type": "1B300BC2-CFFC-47FF-89F9-BD6CCF5F2853", + "format": "uint8", + "iid": 40 + }, + { + "value": "2014-01-03T00:00:00-05:00", + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "1621F556-1367-443C-AF19-82AF018E99DE", + "format": "string", + "iid": 41 + }, + { + "perms": [ + "pw" + ], + "type": "FA128DE6-9D7D-49A4-B6D8-4E4E234DEE38", + "format": "bool", + "iid": 48 + }, + { + "value": 1, + "perms": [ + "pr", + "ev" + ], + "type": "4A6AE4F6-036C-495D-87CC-B3702B437741", + "format": "uint8", + "iid": 49 + }, + { + "value": 0, + "perms": [ + "pr", + "ev" + ], + "type": "DB7BF261-7042-4194-8BD1-3AA22830AEDD", + "format": "uint8", + "iid": 50 + }, + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "41935E3E-B54D-42E9-B8B9-D33C6319F0AF", + "format": "bool", + "iid": 51 + }, + { + "minValue": 0, + "maxValue": 100, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "C35DA3C0-E004-40E3-B153-46655CDD9214", + "value": 0, + "format": "uint8", + "iid": 52 + }, + { + "value": 100, + "perms": [ + "pr", + "ev" + ], + "type": "48F62AEC-4171-4B4A-8F0E-1EEB6708B3FB", + "format": "uint8", + "iid": 53 + }, + { + "value": "The Hive is humming along. You have no pending alerts or reminders.", + "perms": [ + "pr", + "ev" + ], + "iid": 54, + "type": "1B1515F2-CC45-409F-991F-C480987F92C3", + "format": "string", + "maxLen": 256 + } + ], + "iid": 16 + }, + { + "type": "85", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 28 + }, + { + "value": false, + "perms": [ + "pr", + "ev" + ], + "type": "22", + "format": "bool", + "iid": 66 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "value": 2980, + "format": "int", + "iid": 67 + } + ], + "iid": 56 + }, + { + "type": "86", + "characteristics": [ + { + "value": "HomeW", + "perms": [ + "pr" + ], + "type": "23", + "format": "string", + "iid": 29 + }, + { + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "perms": [ + "pr", + "ev" + ], + "type": "71", + "value": 1, + "format": "uint8", + "iid": 65 + }, + { + "minValue": -1, + "maxValue": 86400, + "perms": [ + "pr", + "ev" + ], + "type": "A8f798E0-4A40-11E6-BDF4-0800200C9A66", + "value": 2980, + "format": "int", + "iid": 68 + } + ], + "iid": 57 + } + ] + } +] \ No newline at end of file diff --git a/tests/components/homekit_controller/specific_devices/koogeek_ls1.json b/tests/fixtures/homekit_controller/koogeek_ls1.json similarity index 100% rename from tests/components/homekit_controller/specific_devices/koogeek_ls1.json rename to tests/fixtures/homekit_controller/koogeek_ls1.json diff --git a/tests/fixtures/homekit_controller/lennox_e30.json b/tests/fixtures/homekit_controller/lennox_e30.json new file mode 100644 index 00000000000000..9d2fe1152598ce --- /dev/null +++ b/tests/fixtures/homekit_controller/lennox_e30.json @@ -0,0 +1,196 @@ +[ + { + "aid": 1, + "services": [ + { + "characteristics": [ + { + "format": "bool", + "iid": 2, + "perms": [ + "pw" + ], + "type": "14" + }, + { + "format": "string", + "iid": 3, + "perms": [ + "pr" + ], + "type": "20", + "value": "Lennox" + }, + { + "format": "string", + "iid": 4, + "perms": [ + "pr" + ], + "type": "21", + "value": "E30 2B" + }, + { + "format": "string", + "iid": 5, + "perms": [ + "pr" + ], + "type": "23", + "value": "Lennox" + }, + { + "format": "string", + "iid": 6, + "perms": [ + "pr" + ], + "type": "30", + "value": "XXXXXXXX" + }, + { + "format": "string", + "iid": 7, + "perms": [ + "pr" + ], + "type": "52", + "value": "3.40.XX" + }, + { + "format": "string", + "iid": 8, + "perms": [ + "pr" + ], + "type": "53", + "value": "3.0.XX" + } + ], + "iid": 1, + "type": "3E" + }, + { + "characteristics": [ + { + "format": "uint8", + "iid": 101, + "maxValue": 2, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "ev" + ], + "type": "F", + "value": 1 + }, + { + "format": "uint8", + "iid": 102, + "maxValue": 3, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "33", + "value": 3 + }, + { + "format": "float", + "iid": 103, + "maxValue": 100, + "minStep": 0.1, + "minValue": 0, + "perms": [ + "pr", + "ev" + ], + "type": "11", + "unit": "celsius", + "value": 20.5 + }, + { + "format": "float", + "iid": 104, + "maxValue": 32, + "minStep": 0.5, + "minValue": 4.5, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "35", + "unit": "celsius", + "value": 21 + }, + { + "format": "uint8", + "iid": 105, + "maxValue": 1, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "36", + "value": 0 + }, + { + "format": "float", + "iid": 106, + "maxValue": 37, + "minStep": 0.5, + "minValue": 16, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "D", + "unit": "celsius", + "value": 29.5 + }, + { + "format": "float", + "iid": 107, + "maxValue": 100, + "minStep": 1, + "minValue": 0, + "perms": [ + "pr", + "ev" + ], + "type": "10", + "unit": "percentage", + "value": 34 + }, + { + "format": "float", + "iid": 108, + "maxValue": 32, + "minStep": 0.5, + "minValue": 4.5, + "perms": [ + "pr", + "pw", + "ev" + ], + "type": "12", + "unit": "celsius", + "value": 21 + } + ], + "iid": 100, + "primary": true, + "type": "4A" + } + ] + } +] \ No newline at end of file From b6ac964df38197d63f698d81328d17a6fc560ab5 Mon Sep 17 00:00:00 2001 From: Marco Orovecchia Date: Sat, 30 Mar 2019 12:08:30 +0100 Subject: [PATCH 282/605] Added support for transitions for nanoleaf light (#22192) * Added transition support for nanoleaf * Formatting for comments * Inline comment instead of additional line * Set color_temp before starting transition --- homeassistant/components/nanoleaf/light.py | 30 ++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/nanoleaf/light.py b/homeassistant/components/nanoleaf/light.py index 8cb899f1d285c1..818617f1b9adf6 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -10,8 +10,9 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, - PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, Light) + ATTR_TRANSITION, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, + SUPPORT_TRANSITION, Light) from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN import homeassistant.helpers.config_validation as cv from homeassistant.util import color as color_util @@ -32,7 +33,7 @@ ICON = 'mdi:triangle-outline' SUPPORT_NANOLEAF = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT | - SUPPORT_COLOR) + SUPPORT_COLOR | SUPPORT_TRANSITION) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, @@ -171,27 +172,40 @@ def supported_features(self): def turn_on(self, **kwargs): """Instruct the light to turn on.""" - self._light.on = True brightness = kwargs.get(ATTR_BRIGHTNESS) hs_color = kwargs.get(ATTR_HS_COLOR) color_temp_mired = kwargs.get(ATTR_COLOR_TEMP) effect = kwargs.get(ATTR_EFFECT) + transition = kwargs.get(ATTR_TRANSITION) if hs_color: hue, saturation = hs_color self._light.hue = int(hue) self._light.saturation = int(saturation) - if color_temp_mired: self._light.color_temperature = mired_to_kelvin(color_temp_mired) - if brightness: - self._light.brightness = int(brightness / 2.55) + + if transition: + if brightness: # tune to the required brightness in n seconds + self._light.brightness_transition( + int(brightness / 2.55), int(transition)) + else: # If brightness is not specified, assume full brightness + self._light.brightness_transition(100, int(transition)) + else: # If no transition is occurring, turn on the light + self._light.on = True + if brightness: + self._light.brightness = int(brightness / 2.55) + if effect: self._light.effect = effect def turn_off(self, **kwargs): """Instruct the light to turn off.""" - self._light.on = False + transition = kwargs.get(ATTR_TRANSITION) + if transition: + self._light.brightness_transition(0, int(transition)) + else: + self._light.on = False def update(self): """Fetch new state data for this light.""" From ecba87179f1248c6669874ffad8b6744010646cf Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Sat, 30 Mar 2019 08:52:17 -0500 Subject: [PATCH 283/605] Add Heos config flow (#22554) * Add UI initiated config flow * Fix alpha order --- .../components/heos/.translations/en.json | 17 +++++- homeassistant/components/heos/__init__.py | 2 + homeassistant/components/heos/config_flow.py | 35 ++++++++++++ homeassistant/components/heos/strings.json | 17 +++++- homeassistant/config_entries.py | 1 + tests/components/heos/test_config_flow.py | 57 +++++++++++++++++++ tests/components/heos/test_init.py | 10 ++++ 7 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 tests/components/heos/test_config_flow.py diff --git a/homeassistant/components/heos/.translations/en.json b/homeassistant/components/heos/.translations/en.json index de440ec611aeda..a272c0a2a0fd88 100644 --- a/homeassistant/components/heos/.translations/en.json +++ b/homeassistant/components/heos/.translations/en.json @@ -1,5 +1,20 @@ { "config": { - "title": "Heos" + "title": "Heos", + "step": { + "user": { + "title": "Connect to Heos", + "description": "Please enter the host name or IP address of a Heos device (preferably one connected via wire to the network).", + "data": { + "access_token": "Host" + } + } + }, + "error": { + "connection_failure": "Unable to connect to the specified host." + }, + "abort": { + "already_setup": "You can only configure a single Heos connection as it will support all devices on the network." + } } } \ No newline at end of file diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index 536b4f8623b565..2214a602ef331f 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -27,6 +27,8 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the HEOS component.""" + if DOMAIN not in config: + return True host = config[DOMAIN][CONF_HOST] entries = hass.config_entries.async_entries(DOMAIN) if not entries: diff --git a/homeassistant/components/heos/config_flow.py b/homeassistant/components/heos/config_flow.py index 9c4cfc211ae727..5fd7ea59912f15 100644 --- a/homeassistant/components/heos/config_flow.py +++ b/homeassistant/components/heos/config_flow.py @@ -1,4 +1,8 @@ """Config flow to configure Heos.""" +import asyncio + +import voluptuous as vol + from homeassistant import config_entries from homeassistant.const import CONF_HOST @@ -23,3 +27,34 @@ async def async_step_import(self, user_input=None): return self.async_create_entry( title=format_title(host), data={CONF_HOST: host}) + + async def async_step_user(self, user_input=None): + """Obtain host and validate connection.""" + from pyheos import Heos + + # Only a single entry is supported + entries = self.hass.config_entries.async_entries(DOMAIN) + if entries: + return self.async_abort(reason='already_setup') + + # Try connecting to host if provided + errors = {} + host = None + if user_input is not None: + host = user_input[CONF_HOST] + heos = Heos(host) + try: + await heos.connect() + return await self.async_step_import(user_input) + except (asyncio.TimeoutError, ConnectionError): + errors[CONF_HOST] = 'connection_failure' + finally: + await heos.disconnect() + + # Return form + return self.async_show_form( + step_id='user', + data_schema=vol.Schema({ + vol.Required(CONF_HOST, default=host): str + }), + errors=errors) diff --git a/homeassistant/components/heos/strings.json b/homeassistant/components/heos/strings.json index de440ec611aeda..a272c0a2a0fd88 100644 --- a/homeassistant/components/heos/strings.json +++ b/homeassistant/components/heos/strings.json @@ -1,5 +1,20 @@ { "config": { - "title": "Heos" + "title": "Heos", + "step": { + "user": { + "title": "Connect to Heos", + "description": "Please enter the host name or IP address of a Heos device (preferably one connected via wire to the network).", + "data": { + "access_token": "Host" + } + } + }, + "error": { + "connection_failure": "Unable to connect to the specified host." + }, + "abort": { + "already_setup": "You can only configure a single Heos connection as it will support all devices on the network." + } } } \ No newline at end of file diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index df635807abe39c..d0d48c0f764076 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -153,6 +153,7 @@ async def async_step_discovery(info): 'geofency', 'gpslogger', 'hangouts', + 'heos', 'homematicip_cloud', 'hue', 'ifttt', diff --git a/tests/components/heos/test_config_flow.py b/tests/components/heos/test_config_flow.py new file mode 100644 index 00000000000000..8314ad07bc22b1 --- /dev/null +++ b/tests/components/heos/test_config_flow.py @@ -0,0 +1,57 @@ +"""Tests for the Heos config flow module.""" +import asyncio + +from homeassistant import data_entry_flow +from homeassistant.components.heos.config_flow import HeosFlowHandler +from homeassistant.const import CONF_HOST + + +async def test_flow_aborts_already_setup(hass, config_entry): + """Test flow aborts when entry already setup.""" + config_entry.add_to_hass(hass) + flow = HeosFlowHandler() + flow.hass = hass + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'already_setup' + + +async def test_no_host_shows_form(hass): + """Test form is shown when host not provided.""" + flow = HeosFlowHandler() + flow.hass = hass + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'user' + assert result['errors'] == {} + + +async def test_cannot_connect_shows_error_form(hass, controller): + """Test form is shown with error when cannot connect.""" + flow = HeosFlowHandler() + flow.hass = hass + + errors = [ConnectionError, asyncio.TimeoutError] + for error in errors: + controller.connect.side_effect = error + result = await flow.async_step_user({CONF_HOST: '127.0.0.1'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'user' + assert result['errors'][CONF_HOST] == 'connection_failure' + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 + controller.connect.reset_mock() + controller.disconnect.reset_mock() + + +async def test_create_entry_when_host_valid(hass, controller): + """Test result type is create entry when host is valid.""" + flow = HeosFlowHandler() + flow.hass = hass + data = {CONF_HOST: '127.0.0.1'} + result = await flow.async_step_user(data) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'Controller (127.0.0.1)' + assert result['data'] == data + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py index d1932da5abb9ba..b89c39113e4d2b 100644 --- a/tests/components/heos/test_init.py +++ b/tests/components/heos/test_init.py @@ -45,6 +45,16 @@ async def test_async_setup_returns_true(hass, config_entry, config): assert entries[0] == config_entry +async def test_async_setup_no_config_returns_true(hass, config_entry): + """Test component setup updates entry from entry only.""" + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0] == config_entry + + async def test_async_setup_entry_loads_platforms( hass, config_entry, controller): """Test load connects to heos, retrieves players, and loads platforms.""" From 64306922b101a559d389ff31a403d9b5f5ec75ed Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sat, 30 Mar 2019 08:30:21 -0700 Subject: [PATCH 284/605] Fix name conflict in tests (#22556) * Fix name conflict in tests * Lint * Lint --- .../components/config/test_config_entries.py | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 852a5adf6a2cc3..6d2304433ab85f 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -31,10 +31,30 @@ def client(hass, hass_client): yield hass.loop.run_until_complete(hass_client()) +@HANDLERS.register('comp1') +class Comp1ConfigFlow: + """Config flow with options flow.""" + + @staticmethod + @callback + def async_get_options_flow(config, options): + """Get options flow.""" + pass + + +@HANDLERS.register('comp2') +class Comp2ConfigFlow: + """Config flow without options flow.""" + + def __init__(self): + """Init.""" + pass + + async def test_get_entries(hass, client): """Test get entries.""" MockConfigEntry( - domain='comp', + domain='comp1', title='Test 1', source='bla', connection_class=core_ce.CONN_CLASS_LOCAL_POLL, @@ -47,18 +67,6 @@ async def test_get_entries(hass, client): connection_class=core_ce.CONN_CLASS_ASSUMED, ).add_to_hass(hass) - class CompConfigFlow: - @staticmethod - @callback - def async_get_options_flow(config, options): - pass - HANDLERS['comp'] = CompConfigFlow() - - class Comp2ConfigFlow: - def __init__(self): - pass - HANDLERS['comp2'] = Comp2ConfigFlow() - resp = await client.get('/api/config/config_entries/entry') assert resp.status == 200 data = await resp.json() @@ -66,7 +74,7 @@ def __init__(self): entry.pop('entry_id') assert data == [ { - 'domain': 'comp', + 'domain': 'comp1', 'title': 'Test 1', 'source': 'bla', 'state': 'not_loaded', From 2e375aa8024842af732ea9296c577b75d86d24dc Mon Sep 17 00:00:00 2001 From: emontnemery Date: Sat, 30 Mar 2019 18:19:18 +0100 Subject: [PATCH 285/605] Improve handling of audio groups (#22396) * Improve handling of audio groups * Review comments * Fix tests * Add tests * Review comment * Bump pychromecast --- homeassistant/components/cast/__init__.py | 2 +- homeassistant/components/cast/media_player.py | 393 ++++++++++++++++-- requirements_all.txt | 2 +- tests/components/cast/test_media_player.py | 97 +++++ 4 files changed, 461 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/cast/__init__.py b/homeassistant/components/cast/__init__.py index bc32b36c455623..1aea4655e17ddd 100644 --- a/homeassistant/components/cast/__init__.py +++ b/homeassistant/components/cast/__init__.py @@ -2,7 +2,7 @@ from homeassistant import config_entries from homeassistant.helpers import config_entry_flow -REQUIREMENTS = ['pychromecast==3.0.0'] +REQUIREMENTS = ['pychromecast==3.1.0'] DOMAIN = 'cast' diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 77332883a910f6..12f524f2121fc6 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -48,6 +48,8 @@ # Stores UUIDs of cast devices that were added as entities. Doesn't store # None UUIDs. ADDED_CAST_DEVICES_KEY = 'cast_added_cast_devices' +# Stores an audio group manager. +CAST_MULTIZONE_MANAGER_KEY = 'cast_multizone_manager' # Dispatcher signal fired with a ChromecastInfo every time we discover a new # Chromecast or receive it through configuration @@ -79,6 +81,7 @@ class ChromecastInfo: manufacturer = attr.ib(type=str, default='') model_name = attr.ib(type=str, default='') friendly_name = attr.ib(type=Optional[str], default=None) + is_dynamic_group = attr.ib(type=Optional[bool], default=None) @property def is_audio_group(self) -> bool: @@ -88,7 +91,13 @@ def is_audio_group(self) -> bool: @property def is_information_complete(self) -> bool: """Return if all information is filled out.""" - return all(attr.astuple(self)) + want_dynamic_group = self.is_audio_group + have_dynamic_group = self.is_dynamic_group is not None + have_all_except_dynamic_group = all( + attr.astuple(self, filter=attr.filters.exclude( + attr.fields(ChromecastInfo).is_dynamic_group))) + return (have_all_except_dynamic_group and + (not want_dynamic_group or have_dynamic_group)) @property def host_port(self) -> Tuple[str, int]: @@ -96,9 +105,16 @@ def host_port(self) -> Tuple[str, int]: return self.host, self.port +def _is_matching_dynamic_group(our_info: ChromecastInfo, + new_info: ChromecastInfo,) -> bool: + return (our_info.is_audio_group and + new_info.is_dynamic_group and + our_info.friendly_name == new_info.friendly_name) + + def _fill_out_missing_chromecast_info(info: ChromecastInfo) -> ChromecastInfo: """Fill out missing attributes of ChromecastInfo using blocking HTTP.""" - if info.is_information_complete or info.is_audio_group: + if info.is_information_complete: # We have all information, no need to check HTTP API. Or this is an # audio group, so checking via HTTP won't give us any new information. return info @@ -106,6 +122,28 @@ def _fill_out_missing_chromecast_info(info: ChromecastInfo) -> ChromecastInfo: # Fill out missing information via HTTP dial. from pychromecast import dial + if info.is_audio_group: + is_dynamic_group = False + http_group_status = None + dynamic_groups = [] + if info.uuid: + http_group_status = dial.get_multizone_status( + info.host, services=[info.service], + zconf=ChromeCastZeroconf.get_zeroconf()) + if http_group_status is not None: + dynamic_groups = \ + [str(g.uuid) for g in http_group_status.dynamic_groups] + is_dynamic_group = info.uuid in dynamic_groups + + return ChromecastInfo( + service=info.service, host=info.host, port=info.port, + uuid=info.uuid, + friendly_name=info.friendly_name, + manufacturer=info.manufacturer, + model_name=info.model_name, + is_dynamic_group=is_dynamic_group + ) + http_device_status = dial.get_device_status( info.host, services=[info.service], zconf=ChromeCastZeroconf.get_zeroconf()) @@ -218,12 +256,17 @@ def _async_create_cast_device(hass: HomeAssistantType, Returns None if the cast device has already been added. """ + _LOGGER.debug("_async_create_cast_device: %s", info) if info.uuid is None: # Found a cast without UUID, we don't store it because we won't be able # to update it anyway. return CastDevice(info) # Found a cast with UUID + if info.is_dynamic_group: + # This is a dynamic group, do not add it. + return None + added_casts = hass.data[ADDED_CAST_DEVICES_KEY] if info.uuid in added_casts: # Already added this one, the entity will take care of moved hosts @@ -322,15 +365,22 @@ class CastStatusListener: potentially arrive. This class allows invalidating past chromecast objects. """ - def __init__(self, cast_device, chromecast): + def __init__(self, cast_device, chromecast, mz_mgr): """Initialize the status listener.""" self._cast_device = cast_device + self._uuid = chromecast.uuid self._valid = True + self._mz_mgr = mz_mgr chromecast.register_status_listener(self) chromecast.socket_client.media_controller.register_status_listener( self) chromecast.register_connection_listener(self) + # pylint: disable=protected-access + if cast_device._cast_info.is_audio_group: + self._mz_mgr.add_multizone(chromecast) + else: + self._mz_mgr.register_listener(chromecast.uuid, self) def new_cast_status(self, cast_status): """Handle reception of a new CastStatus.""" @@ -347,11 +397,85 @@ def new_connection_status(self, connection_status): if self._valid: self._cast_device.new_connection_status(connection_status) + @staticmethod + def added_to_multizone(group_uuid): + """Handle the cast added to a group.""" + pass + + def removed_from_multizone(self, group_uuid): + """Handle the cast removed from a group.""" + if self._valid: + self._cast_device.multizone_new_media_status(group_uuid, None) + self._cast_device.multizone_new_cast_status(group_uuid, None) + + def multizone_new_cast_status(self, group_uuid, cast_status): + """Handle reception of a new MediaStatus for a group.""" + if self._valid: + self._cast_device.multizone_new_cast_status( + group_uuid, cast_status) + + def multizone_new_media_status(self, group_uuid, media_status): + """Handle reception of a new MediaStatus for a group.""" + if self._valid: + self._cast_device.multizone_new_media_status( + group_uuid, media_status) + def invalidate(self): """Invalidate this status listener. All following callbacks won't be forwarded. """ + # pylint: disable=protected-access + if self._cast_device._cast_info.is_audio_group: + self._mz_mgr.remove_multizone(self._uuid) + else: + self._mz_mgr.deregister_listener(self._uuid, self) + self._valid = False + + +class DynamicGroupCastStatusListener: + """Helper class to handle pychromecast status callbacks. + + Necessary because a CastDevice entity can create a new socket client + and therefore callbacks from multiple chromecast connections can + potentially arrive. This class allows invalidating past chromecast objects. + """ + + def __init__(self, cast_device, chromecast, mz_mgr): + """Initialize the status listener.""" + self._cast_device = cast_device + self._uuid = chromecast.uuid + self._valid = True + self._mz_mgr = mz_mgr + + chromecast.register_status_listener(self) + chromecast.socket_client.media_controller.register_status_listener( + self) + chromecast.register_connection_listener(self) + self._mz_mgr.add_multizone(chromecast) + + def new_cast_status(self, cast_status): + """Handle reception of a new CastStatus.""" + if self._valid: + self._cast_device.new_dynamic_group_cast_status(cast_status) + + def new_media_status(self, media_status): + """Handle reception of a new MediaStatus.""" + if self._valid: + self._cast_device.new_dynamic_group_media_status(media_status) + + def new_connection_status(self, connection_status): + """Handle reception of a new ConnectionStatus.""" + if self._valid: + self._cast_device.new_dynamic_group_connection_status( + connection_status) + + def invalidate(self): + """Invalidate this status listener. + + All following callbacks won't be forwarded. + """ + self._mz_mgr.remove_multizone(self._uuid) self._valid = False @@ -375,8 +499,20 @@ def __init__(self, cast_info): self.cast_status = None self.media_status = None self.media_status_received = None + self._dynamic_group_cast_info = None # type: ChromecastInfo + self._dynamic_group_cast = None \ + # type: Optional[pychromecast.Chromecast] + self.dynamic_group_cast_status = None + self.dynamic_group_media_status = None + self.dynamic_group_media_status_received = None + self.mz_cast_status = {} + self.mz_media_status = {} + self.mz_media_status_received = {} self._available = False # type: bool + self._dynamic_group_available = False # type: bool self._status_listener = None # type: Optional[CastStatusListener] + self._dynamic_group_status_listener = None \ + # type: Optional[CastStatusListener] self._add_remove_handler = None self._del_remove_handler = None @@ -388,6 +524,13 @@ def async_cast_discovered(discover: ChromecastInfo): if self._cast_info.uuid is None: # We can't handle empty UUIDs return + if _is_matching_dynamic_group(self._cast_info, discover): + _LOGGER.debug("Discovered matching dynamic group: %s", + discover) + self.hass.async_create_task( + self.async_set_dynamic_group(discover)) + return + if self._cast_info.uuid != discover.uuid: # Discovered is not our device. return @@ -405,6 +548,11 @@ def async_cast_removed(discover: ChromecastInfo): if self._cast_info.uuid is None: # We can't handle empty UUIDs return + if (self._dynamic_group_cast_info is not None and + self._dynamic_group_cast_info.uuid == discover.uuid): + _LOGGER.debug("Removed matching dynamic group: %s", discover) + self.hass.async_create_task(self.async_del_dynamic_group()) + return if self._cast_info.uuid != discover.uuid: # Removed is not our device. return @@ -423,6 +571,14 @@ async def async_stop(event): async_cast_removed) self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop) self.hass.async_create_task(self.async_set_cast_info(self._cast_info)) + for info in self.hass.data[KNOWN_CHROMECAST_INFO_KEY]: + if _is_matching_dynamic_group(self._cast_info, info): + _LOGGER.debug("[%s %s (%s:%s)] Found dynamic group: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, info) + self.hass.async_create_task( + self.async_set_dynamic_group(info)) + break async def async_will_remove_from_hass(self) -> None: """Disconnect Chromecast object when removed.""" @@ -478,7 +634,14 @@ async def async_set_cast_info(self, cast_info): cast_info.friendly_name )) self._chromecast = chromecast - self._status_listener = CastStatusListener(self, chromecast) + + if CAST_MULTIZONE_MANAGER_KEY not in self.hass.data: + from pychromecast.controllers.multizone import MultizoneManager + self.hass.data[CAST_MULTIZONE_MANAGER_KEY] = MultizoneManager() + mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY] + + self._status_listener = CastStatusListener( + self, chromecast, mz_mgr) self._available = False self.cast_status = chromecast.status self.media_status = chromecast.media_controller.status @@ -493,6 +656,57 @@ async def async_del_cast_info(self, cast_info): self._cast_info.host, self._cast_info.port, cast_info.service, self.services) + async def async_set_dynamic_group(self, cast_info): + """Set the cast information and set up the chromecast object.""" + import pychromecast + _LOGGER.debug( + "[%s %s (%s:%s)] Connecting to dynamic group by host %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, cast_info) + + self.async_del_dynamic_group() + self._dynamic_group_cast_info = cast_info + + # pylint: disable=protected-access + chromecast = await self.hass.async_add_executor_job( + pychromecast._get_chromecast_from_host, ( + cast_info.host, cast_info.port, cast_info.uuid, + cast_info.model_name, cast_info.friendly_name + )) + + self._dynamic_group_cast = chromecast + + if CAST_MULTIZONE_MANAGER_KEY not in self.hass.data: + from pychromecast.controllers.multizone import MultizoneManager + self.hass.data[CAST_MULTIZONE_MANAGER_KEY] = MultizoneManager() + mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY] + + self._dynamic_group_status_listener = DynamicGroupCastStatusListener( + self, chromecast, mz_mgr) + self._dynamic_group_available = False + self.dynamic_group_cast_status = chromecast.status + self.dynamic_group_media_status = chromecast.media_controller.status + self._dynamic_group_cast.start() + self.async_schedule_update_ha_state() + + async def async_del_dynamic_group(self): + """Remove the dynamic group.""" + cast_info = self._dynamic_group_cast_info + _LOGGER.debug("[%s %s (%s:%s)] Remove dynamic group: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, + cast_info.service if cast_info else None) + + self._dynamic_group_available = False + self._dynamic_group_cast_info = None + if self._dynamic_group_cast is not None: + await self.hass.async_add_executor_job( + self._dynamic_group_cast.disconnect) + + self._dynamic_group_invalidate() + + self.async_schedule_update_ha_state() + async def _async_disconnect(self): """Disconnect Chromecast object if it is set.""" if self._chromecast is None: @@ -504,7 +718,10 @@ async def _async_disconnect(self): self._available = False self.async_schedule_update_ha_state() - await self.hass.async_add_job(self._chromecast.disconnect) + await self.hass.async_add_executor_job(self._chromecast.disconnect) + if self._dynamic_group_cast is not None: + await self.hass.async_add_executor_job( + self._dynamic_group_cast.disconnect) self._invalidate() @@ -516,10 +733,23 @@ def _invalidate(self): self.cast_status = None self.media_status = None self.media_status_received = None + self.mz_cast_status = {} + self.mz_media_status = {} + self.mz_media_status_received = {} if self._status_listener is not None: self._status_listener.invalidate() self._status_listener = None + def _dynamic_group_invalidate(self): + """Invalidate some attributes.""" + self._dynamic_group_cast = None + self.dynamic_group_cast_status = None + self.dynamic_group_media_status = None + self.dynamic_group_media_status_received = None + if self._dynamic_group_status_listener is not None: + self._dynamic_group_status_listener.invalidate() + self._dynamic_group_status_listener = None + # ========== Callbacks ========== def new_cast_status(self, cast_status): """Handle updates of the cast status.""" @@ -565,6 +795,67 @@ def new_connection_status(self, connection_status): self._available = new_available self.schedule_update_ha_state() + def new_dynamic_group_cast_status(self, cast_status): + """Handle updates of the cast status.""" + self.dynamic_group_cast_status = cast_status + self.schedule_update_ha_state() + + def new_dynamic_group_media_status(self, media_status): + """Handle updates of the media status.""" + self.dynamic_group_media_status = media_status + self.dynamic_group_media_status_received = dt_util.utcnow() + self.schedule_update_ha_state() + + def new_dynamic_group_connection_status(self, connection_status): + """Handle updates of connection status.""" + from pychromecast.socket_client import CONNECTION_STATUS_CONNECTED, \ + CONNECTION_STATUS_DISCONNECTED + + _LOGGER.debug( + "[%s %s (%s:%s)] Received dynamic group connection status: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, + connection_status.status) + if connection_status.status == CONNECTION_STATUS_DISCONNECTED: + self._dynamic_group_available = False + self._dynamic_group_invalidate() + self.schedule_update_ha_state() + return + + new_available = connection_status.status == CONNECTION_STATUS_CONNECTED + if new_available != self._dynamic_group_available: + # Connection status callbacks happen often when disconnected. + # Only update state when availability changed to put less pressure + # on state machine. + _LOGGER.debug( + "[%s %s (%s:%s)] Dynamic group availability changed: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, + connection_status.status) + self._dynamic_group_available = new_available + self.schedule_update_ha_state() + + def multizone_new_media_status(self, group_uuid, media_status): + """Handle updates of audio group media status.""" + _LOGGER.debug( + "[%s %s (%s:%s)] Multizone %s media status: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, + group_uuid, media_status) + self.mz_media_status[group_uuid] = media_status + self.mz_media_status_received[group_uuid] = dt_util.utcnow() + self.schedule_update_ha_state() + + def multizone_new_cast_status(self, group_uuid, cast_status): + """Handle updates of audio group status.""" + _LOGGER.debug( + "[%s %s (%s:%s)] Multizone %s cast status: %s", + self.entity_id, self._cast_info.friendly_name, + self._cast_info.host, self._cast_info.port, + group_uuid, cast_status) + self.mz_cast_status[group_uuid] = cast_status + self.schedule_update_ha_state() + # ========== Service Calls ========== def turn_on(self): """Turn on the cast device.""" @@ -650,16 +941,42 @@ def device_info(self): 'manufacturer': cast_info.manufacturer, } + def _media_status(self): + """ + Return media status. + + First try from our own cast, then dynamic groups and finally + groups which our cast is a member in. + """ + media_status = self.media_status + media_status_received = self.media_status_received + + if media_status is None or media_status.player_state == "UNKNOWN": + media_status = self.dynamic_group_media_status + media_status_received = self.dynamic_group_media_status_received + + if media_status is None or media_status.player_state == "UNKNOWN": + groups = self.mz_media_status + for k, val in groups.items(): + if val and val.player_state != "UNKNOWN": + media_status = val + media_status_received = self.mz_media_status_received[k] + break + + return (media_status, media_status_received) + @property def state(self): """Return the state of the player.""" - if self.media_status is None: + media_status, _ = self._media_status() + + if media_status is None: return None - if self.media_status.player_is_playing: + if media_status.player_is_playing: return STATE_PLAYING - if self.media_status.player_is_paused: + if media_status.player_is_paused: return STATE_PAUSED - if self.media_status.player_is_idle: + if media_status.player_is_idle: return STATE_IDLE if self._chromecast is not None and self._chromecast.is_idle: return STATE_OFF @@ -683,75 +1000,87 @@ def is_volume_muted(self): @property def media_content_id(self): """Content ID of current playing media.""" - return self.media_status.content_id if self.media_status else None + media_status, _ = self._media_status() + return media_status.content_id if media_status else None @property def media_content_type(self): """Content type of current playing media.""" - if self.media_status is None: + media_status, _ = self._media_status() + if media_status is None: return None - if self.media_status.media_is_tvshow: + if media_status.media_is_tvshow: return MEDIA_TYPE_TVSHOW - if self.media_status.media_is_movie: + if media_status.media_is_movie: return MEDIA_TYPE_MOVIE - if self.media_status.media_is_musictrack: + if media_status.media_is_musictrack: return MEDIA_TYPE_MUSIC return None @property def media_duration(self): """Duration of current playing media in seconds.""" - return self.media_status.duration if self.media_status else None + media_status, _ = self._media_status() + return media_status.duration if media_status else None @property def media_image_url(self): """Image url of current playing media.""" - if self.media_status is None: + media_status, _ = self._media_status() + if media_status is None: return None - images = self.media_status.images + images = media_status.images return images[0].url if images and images[0].url else None @property def media_title(self): """Title of current playing media.""" - return self.media_status.title if self.media_status else None + media_status, _ = self._media_status() + return media_status.title if media_status else None @property def media_artist(self): """Artist of current playing media (Music track only).""" - return self.media_status.artist if self.media_status else None + media_status, _ = self._media_status() + return media_status.artist if media_status else None @property def media_album_name(self): """Album of current playing media (Music track only).""" - return self.media_status.album_name if self.media_status else None + media_status, _ = self._media_status() + return media_status.album_name if media_status else None @property def media_album_artist(self): """Album artist of current playing media (Music track only).""" - return self.media_status.album_artist if self.media_status else None + media_status, _ = self._media_status() + return media_status.album_artist if media_status else None @property def media_track(self): """Track number of current playing media (Music track only).""" - return self.media_status.track if self.media_status else None + media_status, _ = self._media_status() + return media_status.track if media_status else None @property def media_series_title(self): """Return the title of the series of current playing media.""" - return self.media_status.series_title if self.media_status else None + media_status, _ = self._media_status() + return media_status.series_title if media_status else None @property def media_season(self): """Season of current playing media (TV Show only).""" - return self.media_status.season if self.media_status else None + media_status, _ = self._media_status() + return media_status.season if media_status else None @property def media_episode(self): """Episode of current playing media (TV Show only).""" - return self.media_status.episode if self.media_status else None + media_status, _ = self._media_status() + return media_status.episode if media_status else None @property def app_id(self): @@ -771,12 +1100,13 @@ def supported_features(self): @property def media_position(self): """Position of current playing media in seconds.""" - if self.media_status is None or \ - not (self.media_status.player_is_playing or - self.media_status.player_is_paused or - self.media_status.player_is_idle): + media_status, _ = self._media_status() + if media_status is None or \ + not (media_status.player_is_playing or + media_status.player_is_paused or + media_status.player_is_idle): return None - return self.media_status.current_time + return media_status.current_time @property def media_position_updated_at(self): @@ -784,7 +1114,8 @@ def media_position_updated_at(self): Returns value from homeassistant.util.dt.utcnow(). """ - return self.media_status_received + _, media_status_recevied = self._media_status() + return media_status_recevied @property def unique_id(self) -> Optional[str]: diff --git a/requirements_all.txt b/requirements_all.txt index f105a8a711cb7b..f5b8e86c1296c3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -977,7 +977,7 @@ pycfdns==0.0.1 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==3.0.0 +pychromecast==3.1.0 # homeassistant.components.cmus.media_player pycmus==0.1.1 diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index ff81c05642022b..85012a4a710e98 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -24,12 +24,14 @@ def cast_mock(): """Mock pychromecast.""" with patch.dict('sys.modules', { 'pychromecast': MagicMock(), + 'pychromecast.controllers.multizone': MagicMock(), }): yield # pylint: disable=invalid-name FakeUUID = UUID('57355bce-9364-4aa6-ac1e-eb849dccf9e2') +FakeGroupUUID = UUID('57355bce-9364-4aa6-ac1e-eb849dccf9e3') def get_fake_chromecast(info: ChromecastInfo): @@ -300,6 +302,101 @@ async def test_entity_media_states(hass: HomeAssistantType): assert state.state == 'unknown' +async def test_group_media_states(hass: HomeAssistantType): + """Test media states are read from group if entity has no state.""" + info = get_fake_chromecast_info() + full_info = attr.evolve(info, model_name='google home', + friendly_name='Speaker', uuid=FakeUUID) + + with patch('pychromecast.dial.get_device_status', + return_value=full_info): + chromecast, entity = await async_setup_media_player_cast(hass, info) + + entity._available = True + entity.schedule_update_ha_state() + await hass.async_block_till_done() + + state = hass.states.get('media_player.speaker') + assert state is not None + assert state.name == 'Speaker' + assert state.state == 'unknown' + assert entity.unique_id == full_info.uuid + + group_media_status = MagicMock(images=None) + player_media_status = MagicMock(images=None) + + # Player has no state, group is playing -> Should report 'playing' + group_media_status.player_is_playing = True + entity.multizone_new_media_status(str(FakeGroupUUID), group_media_status) + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'playing' + + # Player is paused, group is playing -> Should report 'paused' + player_media_status.player_is_playing = False + player_media_status.player_is_paused = True + entity.new_media_status(player_media_status) + await hass.async_block_till_done() + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'paused' + + # Player is in unknown state, group is playing -> Should report 'playing' + player_media_status.player_state = "UNKNOWN" + entity.new_media_status(player_media_status) + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'playing' + + +async def test_dynamic_group_media_states(hass: HomeAssistantType): + """Test media states are read from group if entity has no state.""" + info = get_fake_chromecast_info() + full_info = attr.evolve(info, model_name='google home', + friendly_name='Speaker', uuid=FakeUUID) + + with patch('pychromecast.dial.get_device_status', + return_value=full_info): + chromecast, entity = await async_setup_media_player_cast(hass, info) + + entity._available = True + entity.schedule_update_ha_state() + await hass.async_block_till_done() + + state = hass.states.get('media_player.speaker') + assert state is not None + assert state.name == 'Speaker' + assert state.state == 'unknown' + assert entity.unique_id == full_info.uuid + + group_media_status = MagicMock(images=None) + player_media_status = MagicMock(images=None) + + # Player has no state, dynamic group is playing -> Should report 'playing' + group_media_status.player_is_playing = True + entity.new_dynamic_group_media_status(group_media_status) + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'playing' + + # Player is paused, dynamic group is playing -> Should report 'paused' + player_media_status.player_is_playing = False + player_media_status.player_is_paused = True + entity.new_media_status(player_media_status) + await hass.async_block_till_done() + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'paused' + + # Player is in unknown state, dynamic group is playing -> Should report + # 'playing' + player_media_status.player_state = "UNKNOWN" + entity.new_media_status(player_media_status) + await hass.async_block_till_done() + state = hass.states.get('media_player.speaker') + assert state.state == 'playing' + + async def test_disconnect_on_stop(hass: HomeAssistantType): """Test cast device disconnects socket on stop.""" info = get_fake_chromecast_info() From 34324afbde4fa0cf8309ca55c5be58720b23a8c1 Mon Sep 17 00:00:00 2001 From: carstenschroeder Date: Sat, 30 Mar 2019 18:47:39 +0100 Subject: [PATCH 286/605] Prevent toogle to false at restart of ADS platforms (#22522) * Prevent toogle to false at restart * change to asyncio.run_coroutine_threadsafe --- homeassistant/components/ads/binary_sensor.py | 25 ++++++++++++++-- homeassistant/components/ads/light.py | 29 ++++++++++++++++--- homeassistant/components/ads/switch.py | 27 +++++++++++++++-- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/ads/binary_sensor.py b/homeassistant/components/ads/binary_sensor.py index 3b935156d1861f..91cd60771d9318 100644 --- a/homeassistant/components/ads/binary_sensor.py +++ b/homeassistant/components/ads/binary_sensor.py @@ -1,5 +1,7 @@ """Support for ADS binary sensors.""" import logging +import asyncio +import async_timeout import voluptuous as vol @@ -41,10 +43,11 @@ def __init__(self, ads_hub, name, ads_var, device_class): """Initialize ADS binary sensor.""" self._name = name self._unique_id = ads_var - self._state = False + self._state = None self._device_class = device_class or 'moving' self._ads_hub = ads_hub self.ads_var = ads_var + self._event = None async def async_added_to_hass(self): """Register device notification.""" @@ -52,11 +55,24 @@ def update(name, value): """Handle device notifications.""" _LOGGER.debug('Variable %s changed its value to %d', name, value) self._state = value + asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) self.schedule_update_ha_state() - self.hass.async_add_job( + async def async_event_set(): + """Set event in async context.""" + self._event.set() + + self._event = asyncio.Event() + + await self.hass.async_add_executor_job( self._ads_hub.add_device_notification, self.ads_var, self._ads_hub.PLCTYPE_BOOL, update) + try: + with async_timeout.timeout(10): + await self._event.wait() + except asyncio.TimeoutError: + _LOGGER.debug('Variable %s: Timeout during first update', + self.ads_var) @property def name(self): @@ -82,3 +98,8 @@ def is_on(self): def should_poll(self): """Return False because entity pushes its state to HA.""" return False + + @property + def available(self): + """Return False if state has not been updated yet.""" + return self._state is not None diff --git a/homeassistant/components/ads/light.py b/homeassistant/components/ads/light.py index c61fd813634027..2ece1402907108 100644 --- a/homeassistant/components/ads/light.py +++ b/homeassistant/components/ads/light.py @@ -1,5 +1,7 @@ """Support for ADS light sources.""" import logging +import asyncio +import async_timeout import voluptuous as vol @@ -30,7 +32,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = config.get(CONF_NAME) add_entities([AdsLight(ads_hub, ads_var_enable, ads_var_brightness, - name)], True) + name)]) class AdsLight(Light): @@ -39,12 +41,13 @@ class AdsLight(Light): def __init__(self, ads_hub, ads_var_enable, ads_var_brightness, name): """Initialize AdsLight entity.""" self._ads_hub = ads_hub - self._on_state = False + self._on_state = None self._brightness = None self._name = name self._unique_id = ads_var_enable self.ads_var_enable = ads_var_enable self.ads_var_brightness = ads_var_brightness + self._event = None async def async_added_to_hass(self): """Register device notification.""" @@ -52,24 +55,37 @@ def update_on_state(name, value): """Handle device notifications for state.""" _LOGGER.debug('Variable %s changed its value to %d', name, value) self._on_state = value + asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) self.schedule_update_ha_state() + async def async_event_set(): + """Set event in async context.""" + self._event.set() + def update_brightness(name, value): """Handle device notification for brightness.""" _LOGGER.debug('Variable %s changed its value to %d', name, value) self._brightness = value self.schedule_update_ha_state() - self.hass.async_add_executor_job( + self._event = asyncio.Event() + + await self.hass.async_add_executor_job( self._ads_hub.add_device_notification, self.ads_var_enable, self._ads_hub.PLCTYPE_BOOL, update_on_state ) if self.ads_var_brightness is not None: - self.hass.async_add_executor_job( + await self.hass.async_add_executor_job( self._ads_hub.add_device_notification, self.ads_var_brightness, self._ads_hub.PLCTYPE_INT, update_brightness ) + try: + with async_timeout.timeout(10): + await self._event.wait() + except asyncio.TimeoutError: + _LOGGER.debug('Variable %s: Timeout during first update', + self.ads_var_enable) @property def name(self): @@ -104,6 +120,11 @@ def supported_features(self): support = SUPPORT_BRIGHTNESS return support + @property + def available(self): + """Return False if state has not been updated yet.""" + return self._on_state is not None + def turn_on(self, **kwargs): """Turn the light on or set a specific dimmer value.""" brightness = kwargs.get(ATTR_BRIGHTNESS) diff --git a/homeassistant/components/ads/switch.py b/homeassistant/components/ads/switch.py index 094b455234956b..3d2189d2ede93a 100644 --- a/homeassistant/components/ads/switch.py +++ b/homeassistant/components/ads/switch.py @@ -1,5 +1,7 @@ """Support for ADS switch platform.""" import logging +import asyncio +import async_timeout import voluptuous as vol @@ -29,7 +31,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = config.get(CONF_NAME) ads_var = config.get(CONF_ADS_VAR) - add_entities([AdsSwitch(ads_hub, name, ads_var)], True) + add_entities([AdsSwitch(ads_hub, name, ads_var)]) class AdsSwitch(ToggleEntity): @@ -38,10 +40,11 @@ class AdsSwitch(ToggleEntity): def __init__(self, ads_hub, name, ads_var): """Initialize the AdsSwitch entity.""" self._ads_hub = ads_hub - self._on_state = False + self._on_state = None self._name = name self._unique_id = ads_var self.ads_var = ads_var + self._event = None async def async_added_to_hass(self): """Register device notification.""" @@ -49,11 +52,24 @@ def update(name, value): """Handle device notification.""" _LOGGER.debug("Variable %s changed its value to %d", name, value) self._on_state = value + asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) self.schedule_update_ha_state() - self.hass.async_add_job( + async def async_event_set(): + """Set event in async context.""" + self._event.set() + + self._event = asyncio.Event() + + await self.hass.async_add_executor_job( self._ads_hub.add_device_notification, self.ads_var, self._ads_hub.PLCTYPE_BOOL, update) + try: + with async_timeout.timeout(10): + await self._event.wait() + except asyncio.TimeoutError: + _LOGGER.debug('Variable %s: Timeout during first update', + self.ads_var) @property def is_on(self): @@ -75,6 +91,11 @@ def should_poll(self): """Return False because entity pushes its state to HA.""" return False + @property + def available(self): + """Return False if state has not been updated yet.""" + return self._on_state is not None + def turn_on(self, **kwargs): """Turn the switch on.""" self._ads_hub.write_by_name( From 2eafa5f81a7fce9105d6a2d19f16cfa0f969e955 Mon Sep 17 00:00:00 2001 From: Klaudiusz Staniek Date: Sat, 30 Mar 2019 18:49:59 +0100 Subject: [PATCH 287/605] Ampio Smog Air Quality Sensor (#21152) * Initial commit for Ampio Smog Air Quality Sensor * coveragerc and requirements_all update * Lint fixed * Moved to vendor folder * Updated according to review * Docs string fix * Docstring fix * Docstring fix * Requirements fixed * Lint fix * .coveragerc updated --- .coveragerc | 1 + homeassistant/components/ampio/__init__.py | 1 + homeassistant/components/ampio/air_quality.py | 97 +++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 102 insertions(+) create mode 100644 homeassistant/components/ampio/__init__.py create mode 100644 homeassistant/components/ampio/air_quality.py diff --git a/.coveragerc b/.coveragerc index 3cba85193140e5..145efe5c847a4b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -23,6 +23,7 @@ omit = homeassistant/components/alpha_vantage/sensor.py homeassistant/components/ambient_station/* homeassistant/components/amcrest/* + homeassistant/components/ampio/* homeassistant/components/android_ip_webcam/* homeassistant/components/androidtv/* homeassistant/components/anel_pwrctrl/switch.py diff --git a/homeassistant/components/ampio/__init__.py b/homeassistant/components/ampio/__init__.py new file mode 100644 index 00000000000000..5f7bb4a44fa2a9 --- /dev/null +++ b/homeassistant/components/ampio/__init__.py @@ -0,0 +1 @@ +"""The Ampio component.""" diff --git a/homeassistant/components/ampio/air_quality.py b/homeassistant/components/ampio/air_quality.py new file mode 100644 index 00000000000000..f7aa98aec7c27c --- /dev/null +++ b/homeassistant/components/ampio/air_quality.py @@ -0,0 +1,97 @@ +"""Support for Ampio Air Quality data.""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.air_quality import ( + PLATFORM_SCHEMA, AirQualityEntity) +from homeassistant.const import CONF_NAME +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle + +REQUIREMENTS = ['asmog==0.0.6'] + +_LOGGER = logging.getLogger(__name__) + +ATTRIBUTION = 'Data provided by Ampio' +CONF_STATION_ID = 'station_id' +SCAN_INTERVAL = timedelta(minutes=10) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_STATION_ID): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the Ampio Smog air quality platform.""" + from asmog import AmpioSmog + + name = config.get(CONF_NAME) + station_id = config[CONF_STATION_ID] + + session = async_get_clientsession(hass) + api = AmpioSmogMapData(AmpioSmog(station_id, hass.loop, session)) + + await api.async_update() + + if not api.api.data: + _LOGGER.error("Station %s is not available", station_id) + return + + async_add_entities([AmpioSmogQuality(api, station_id, name)], True) + + +class AmpioSmogQuality(AirQualityEntity): + """Implementation of an Ampio Smog air quality entity.""" + + def __init__(self, api, station_id, name): + """Initialize the air quality entity.""" + self._ampio = api + self._station_id = station_id + self._name = name or api.api.name + + @property + def name(self): + """Return the name of the air quality entity.""" + return self._name + + @property + def unique_id(self): + """Return unique_name.""" + return "ampio_smog_{}".format(self._station_id) + + @property + def particulate_matter_2_5(self): + """Return the particulate matter 2.5 level.""" + return self._ampio.api.pm2_5 + + @property + def particulate_matter_10(self): + """Return the particulate matter 10 level.""" + return self._ampio.api.pm10 + + @property + def attribution(self): + """Return the attribution.""" + return ATTRIBUTION + + async def async_update(self): + """Get the latest data from the AmpioMap API.""" + await self._ampio.async_update() + + +class AmpioSmogMapData: + """Get the latest data and update the states.""" + + def __init__(self, api): + """Initialize the data object.""" + self.api = api + + @Throttle(SCAN_INTERVAL) + async def async_update(self): + """Get the latest data from AmpioMap.""" + await self.api.get_data() diff --git a/requirements_all.txt b/requirements_all.txt index f5b8e86c1296c3..7ce421cacd9071 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -175,6 +175,9 @@ apns2==0.3.0 # homeassistant.components.aqualogic aqualogic==1.0 +# homeassistant.components.ampio.air_quality +asmog==0.0.6 + # homeassistant.components.asterisk_mbox asterisk_mbox==0.5.0 From 9bf0f60784959c4736ab3c13e5f7160c90a69051 Mon Sep 17 00:00:00 2001 From: Greg Dowling Date: Sat, 30 Mar 2019 17:52:54 +0000 Subject: [PATCH 288/605] Bump pyloopenergy library to 0.1.2 (#22561) --- homeassistant/components/loopenergy/sensor.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/loopenergy/sensor.py b/homeassistant/components/loopenergy/sensor.py index 2ee94249b4c70e..fefba2972f2cc9 100644 --- a/homeassistant/components/loopenergy/sensor.py +++ b/homeassistant/components/loopenergy/sensor.py @@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyloopenergy==0.1.0'] +REQUIREMENTS = ['pyloopenergy==0.1.2'] CONF_ELEC = 'electricity' CONF_GAS = 'gas' diff --git a/requirements_all.txt b/requirements_all.txt index 7ce421cacd9071..ec4c602835635a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1141,7 +1141,7 @@ pylinky==0.3.3 pylitejet==0.1 # homeassistant.components.loopenergy.sensor -pyloopenergy==0.1.0 +pyloopenergy==0.1.2 # homeassistant.components.lutron_caseta pylutron-caseta==0.5.0 From e70931da67381664a1bfb1bae10a914f7b9780b6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 30 Mar 2019 12:30:35 -0700 Subject: [PATCH 289/605] Delete azure-pipelines.yml --- azure-pipelines.yml | 78 --------------------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 1746ca7a8a27d5..00000000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,78 +0,0 @@ -# Python package -# Create and test a Python package on multiple Python versions. -# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/python - -trigger: -- master - -jobs: - -- job: 'Lint' - pool: - vmImage: 'Ubuntu-16.04' - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.5' - architecture: 'x64' - - - script: python -m pip install --upgrade pip && pip install -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt - displayName: 'Install dependencies' - - - script: | - python script/gen_requirements_all.py validate - flake8 - pydocstyle tests - pylint homeassistant - displayName: 'lint' - -- job: 'Test' - pool: - vmImage: 'Ubuntu-16.04' - strategy: - matrix: - Python35: - python.version: '3.5' - Python36: - python.version: '3.6' - Python37: - python.version: '3.7' - maxParallel: 4 - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '$(python.version)' - architecture: 'x64' - - - script: python -m pip install --upgrade pip && pip install -r requirements_test_all.txt -c homeassistant/package_constraints.txt - displayName: 'Install dependencies' - - - script: | - pytest tests - displayName: 'pytest' - - # - task: PublishTestResults@2 - # inputs: - # testResultsFiles: '**/test-results.xml' - # testRunTitle: 'Python $(python.version)' - # condition: succeededOrFailed() - -- job: 'Publish' - dependsOn: 'Test' - pool: - vmImage: 'Ubuntu-16.04' - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.x' - architecture: 'x64' - - - script: python -m pip install --upgrade pip && pip install wheel - displayName: 'Install dependencies' - - - script: python setup.py sdist - displayName: 'Build sdist' From 71ecaa438583db5027a4344e2baf5fa55b70b6f7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 30 Mar 2019 21:59:15 +0100 Subject: [PATCH 290/605] Delete main.workflow --- .github/main.workflow | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 .github/main.workflow diff --git a/.github/main.workflow b/.github/main.workflow deleted file mode 100644 index 54869682e1cada..00000000000000 --- a/.github/main.workflow +++ /dev/null @@ -1,41 +0,0 @@ -workflow "Python 3.7 - tox" { - resolves = ["Python 3.7 - tests"] - on = "push" -} - -action "Python 3.7 - tests" { - uses = "home-assistant/actions/py37-tox@master" - args = "-e py37" -} - -workflow "Python 3.6 - tox" { - resolves = ["Python 3.6 - tests"] - on = "push" -} - -action "Python 3.6 - tests" { - uses = "home-assistant/actions/py36-tox@master" - args = "-e py36" -} - -workflow "Python 3.5 - tox" { - resolves = ["Pyton 3.5 - typing"] - on = "push" -} - -action "Python 3.5 - tests" { - uses = "home-assistant/actions/py35-tox@master" - args = "-e py35" -} - -action "Python 3.5 - lints" { - uses = "home-assistant/actions/py35-tox@master" - needs = ["Python 3.5 - tests"] - args = "-e lint" -} - -action "Pyton 3.5 - typing" { - uses = "home-assistant/actions/py35-tox@master" - args = "-e typing" - needs = ["Python 3.5 - lints"] -} From 54777a81bca5a099d1d56e25e30118dace054db7 Mon Sep 17 00:00:00 2001 From: emontnemery Date: Sun, 31 Mar 2019 05:07:01 +0200 Subject: [PATCH 291/605] Forward media control to playing group (#22566) * Forward media control to playing group * Fix forwarding control to dynamic group * Fix, add tests --- homeassistant/components/cast/media_player.py | 53 +++++++-- tests/components/cast/test_media_player.py | 106 ++++++++++++++++++ 2 files changed, 150 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 12f524f2121fc6..cb60cdc296727f 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -508,11 +508,12 @@ def __init__(self, cast_info): self.mz_cast_status = {} self.mz_media_status = {} self.mz_media_status_received = {} + self.mz_mgr = None self._available = False # type: bool self._dynamic_group_available = False # type: bool self._status_listener = None # type: Optional[CastStatusListener] self._dynamic_group_status_listener = None \ - # type: Optional[CastStatusListener] + # type: Optional[DynamicGroupCastStatusListener] self._add_remove_handler = None self._del_remove_handler = None @@ -638,10 +639,10 @@ async def async_set_cast_info(self, cast_info): if CAST_MULTIZONE_MANAGER_KEY not in self.hass.data: from pychromecast.controllers.multizone import MultizoneManager self.hass.data[CAST_MULTIZONE_MANAGER_KEY] = MultizoneManager() - mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY] + self.mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY] self._status_listener = CastStatusListener( - self, chromecast, mz_mgr) + self, chromecast, self.mz_mgr) self._available = False self.cast_status = chromecast.status self.media_status = chromecast.media_controller.status @@ -736,6 +737,7 @@ def _invalidate(self): self.mz_cast_status = {} self.mz_media_status = {} self.mz_media_status_received = {} + self.mz_mgr = None if self._status_listener is not None: self._status_listener.invalidate() self._status_listener = None @@ -857,6 +859,32 @@ def multizone_new_cast_status(self, group_uuid, cast_status): self.schedule_update_ha_state() # ========== Service Calls ========== + def _media_controller(self): + """ + Return media status. + + First try from our own cast, then dynamic groups and finally + groups which our cast is a member in. + """ + media_status = self.media_status + media_controller = self._chromecast.media_controller + + if ((media_status is None or media_status.player_state == "UNKNOWN") + and self._dynamic_group_cast is not None): + media_status = self.dynamic_group_media_status + media_controller = \ + self._dynamic_group_cast.media_controller + + if media_status is None or media_status.player_state == "UNKNOWN": + groups = self.mz_media_status + for k, val in groups.items(): + if val and val.player_state != "UNKNOWN": + media_controller = \ + self.mz_mgr.get_multizone_mediacontroller(k) + break + + return media_controller + def turn_on(self): """Turn on the cast device.""" import pychromecast @@ -887,30 +915,37 @@ def set_volume_level(self, volume): def media_play(self): """Send play command.""" - self._chromecast.media_controller.play() + media_controller = self._media_controller() + media_controller.play() def media_pause(self): """Send pause command.""" - self._chromecast.media_controller.pause() + media_controller = self._media_controller() + media_controller.pause() def media_stop(self): """Send stop command.""" - self._chromecast.media_controller.stop() + media_controller = self._media_controller() + media_controller.stop() def media_previous_track(self): """Send previous track command.""" - self._chromecast.media_controller.rewind() + media_controller = self._media_controller() + media_controller.rewind() def media_next_track(self): """Send next track command.""" - self._chromecast.media_controller.skip() + media_controller = self._media_controller() + media_controller.skip() def media_seek(self, position): """Seek the media to a specific location.""" - self._chromecast.media_controller.seek(position) + media_controller = self._media_controller() + media_controller.seek(position) def play_media(self, media_type, media_id, **kwargs): """Play media from a URL.""" + # We do not want this to be forwarded to a group / dynamic group self._chromecast.media_controller.play_media(media_id, media_type) # ========== Properties ========== diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index 85012a4a710e98..7c40b09d03ecf8 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -397,6 +397,112 @@ async def test_dynamic_group_media_states(hass: HomeAssistantType): assert state.state == 'playing' +async def test_group_media_control(hass: HomeAssistantType): + """Test media states are read from group if entity has no state.""" + info = get_fake_chromecast_info() + full_info = attr.evolve(info, model_name='google home', + friendly_name='Speaker', uuid=FakeUUID) + + with patch('pychromecast.dial.get_device_status', + return_value=full_info): + chromecast, entity = await async_setup_media_player_cast(hass, info) + + entity._available = True + entity.schedule_update_ha_state() + await hass.async_block_till_done() + + state = hass.states.get('media_player.speaker') + assert state is not None + assert state.name == 'Speaker' + assert state.state == 'unknown' + assert entity.unique_id == full_info.uuid + + group_media_status = MagicMock(images=None) + player_media_status = MagicMock(images=None) + + # Player has no state, group is playing -> Should forward calls to group + group_media_status.player_is_playing = True + entity.multizone_new_media_status(str(FakeGroupUUID), group_media_status) + entity.media_play() + grp_media = entity.mz_mgr.get_multizone_mediacontroller(str(FakeGroupUUID)) + assert grp_media.play.called + assert not chromecast.media_controller.play.called + + # Player is paused, group is playing -> Should not forward + player_media_status.player_is_playing = False + player_media_status.player_is_paused = True + entity.new_media_status(player_media_status) + entity.media_pause() + grp_media = entity.mz_mgr.get_multizone_mediacontroller(str(FakeGroupUUID)) + assert not grp_media.pause.called + assert chromecast.media_controller.pause.called + + # Player is in unknown state, group is playing -> Should forward to group + player_media_status.player_state = "UNKNOWN" + entity.new_media_status(player_media_status) + entity.media_stop() + grp_media = entity.mz_mgr.get_multizone_mediacontroller(str(FakeGroupUUID)) + assert grp_media.stop.called + assert not chromecast.media_controller.stop.called + + # Verify play_media is not forwarded + entity.play_media(None, None) + assert not grp_media.play_media.called + assert chromecast.media_controller.play_media.called + + +async def test_dynamic_group_media_control(hass: HomeAssistantType): + """Test media states are read from group if entity has no state.""" + info = get_fake_chromecast_info() + full_info = attr.evolve(info, model_name='google home', + friendly_name='Speaker', uuid=FakeUUID) + + with patch('pychromecast.dial.get_device_status', + return_value=full_info): + chromecast, entity = await async_setup_media_player_cast(hass, info) + + entity._available = True + entity.schedule_update_ha_state() + entity._dynamic_group_cast = MagicMock() + await hass.async_block_till_done() + + state = hass.states.get('media_player.speaker') + assert state is not None + assert state.name == 'Speaker' + assert state.state == 'unknown' + assert entity.unique_id == full_info.uuid + + group_media_status = MagicMock(images=None) + player_media_status = MagicMock(images=None) + + # Player has no state, dynamic group is playing -> Should forward + group_media_status.player_is_playing = True + entity.new_dynamic_group_media_status(group_media_status) + entity.media_previous_track() + assert entity._dynamic_group_cast.media_controller.rewind.called + assert not chromecast.media_controller.rewind.called + + # Player is paused, dynamic group is playing -> Should not forward + player_media_status.player_is_playing = False + player_media_status.player_is_paused = True + entity.new_media_status(player_media_status) + entity.media_next_track() + assert not entity._dynamic_group_cast.media_controller.skip.called + assert chromecast.media_controller.skip.called + + # Player is in unknown state, dynamic group is playing -> Should forward + player_media_status.player_state = "UNKNOWN" + entity.new_media_status(player_media_status) + entity.media_seek(None) + assert entity._dynamic_group_cast.media_controller.seek.called + assert not chromecast.media_controller.seek.called + + # Verify play_media is not forwarded + entity.play_media(None, None) + assert not entity._dynamic_group_cast.media_controller.play_media.called + assert chromecast.media_controller.play_media.called + + async def test_disconnect_on_stop(hass: HomeAssistantType): """Test cast device disconnects socket on stop.""" info = get_fake_chromecast_info() From f6e9dd48323769a0cff8986ae51016408c0aa6f1 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Sun, 31 Mar 2019 00:01:58 -0400 Subject: [PATCH 292/605] Update Foscam component to support stream source (#22568) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models --- homeassistant/components/foscam/camera.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index ceec57f77557cc..a11d2f48f625b9 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -8,7 +8,8 @@ import voluptuous as vol -from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) +from homeassistant.components.camera import ( + Camera, PLATFORM_SCHEMA, SUPPORT_STREAM) from homeassistant.const import ( CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT) from homeassistant.helpers import config_validation as cv @@ -57,6 +58,8 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) + self._media_port = self._foscam_session.get_port_info()[1]['mediaPort'] + def camera_image(self): """Return a still image response from the camera.""" # Send the request to snap a picture and return raw jpg data @@ -67,6 +70,20 @@ def camera_image(self): return response + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + return 'rtsp://{}:{}@{}:{}/videoMain'.format( + self._username, + self._password, + self._foscam_session.host, + self._media_port) + @property def motion_detection_enabled(self): """Camera Motion Detection Status.""" From 4d1633807c700ca528e79f7a1a676e0d1dc76e1e Mon Sep 17 00:00:00 2001 From: emontnemery Date: Sun, 31 Mar 2019 06:04:32 +0200 Subject: [PATCH 293/605] Turn light off if brightness is 0 (#22400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Turn light off if brightness is 0 * Lint * Review comments * Lint * Fixup, add tests * Fix trådfri light + test --- homeassistant/components/light/__init__.py | 20 ++++++++- homeassistant/components/tradfri/light.py | 2 - tests/components/light/test_init.py | 50 ++++++++++++++++++++++ tests/components/light/test_tradfri.py | 14 ++---- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index acf95a3c081cb2..7fe7ae343f97c1 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -175,6 +175,17 @@ def preprocess_turn_on_alternatives(params): params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) +def preprocess_turn_off(params): + """Process data for turning light off if brightness is 0.""" + if ATTR_BRIGHTNESS in params and params[ATTR_BRIGHTNESS] == 0: + # Zero brightness: Light will be turned off + params = {k: v for k, v in params.items() if k in [ATTR_TRANSITION, + ATTR_FLASH]} + return (True, params) # Light should be turned off + + return (False, None) # Light should be turned on + + class SetIntentHandler(intent.IntentHandler): """Handle set color intents.""" @@ -272,17 +283,24 @@ async def async_handle_light_on_service(service): ) preprocess_turn_on_alternatives(params) + turn_lights_off, off_params = preprocess_turn_off(params) update_tasks = [] for light in target_lights: light.async_set_context(service.context) pars = params + off_pars = off_params + turn_light_off = turn_lights_off if not pars: pars = params.copy() pars[ATTR_PROFILE] = Profiles.get_default(light.entity_id) preprocess_turn_on_alternatives(pars) - await light.async_turn_on(**pars) + turn_light_off, off_pars = preprocess_turn_off(pars) + if turn_light_off: + await light.async_turn_off(**off_pars) + else: + await light.async_turn_on(**pars) if not light.should_poll: continue diff --git a/homeassistant/components/tradfri/light.py b/homeassistant/components/tradfri/light.py index 38ce428b51b7c7..07ab4806dfcc10 100644 --- a/homeassistant/components/tradfri/light.py +++ b/homeassistant/components/tradfri/light.py @@ -263,8 +263,6 @@ async def async_turn_on(self, **kwargs): brightness = kwargs[ATTR_BRIGHTNESS] if brightness > 254: brightness = 254 - elif brightness < 0: - brightness = 0 dimmer_data = {ATTR_DIMMER: brightness, ATTR_TRANSITION_TIME: transition_time} dimmer_command = self._light_control.set_dimmer(**dimmer_data) diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 28d688b2080ee4..0025e9bce666b3 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -161,6 +161,19 @@ def test_services(self): assert not light.is_on(self.hass, dev2.entity_id) assert not light.is_on(self.hass, dev3.entity_id) + # turn off all lights by setting brightness to 0 + common.turn_on(self.hass) + + self.hass.block_till_done() + + common.turn_on(self.hass, brightness=0) + + self.hass.block_till_done() + + assert not light.is_on(self.hass, dev1.entity_id) + assert not light.is_on(self.hass, dev2.entity_id) + assert not light.is_on(self.hass, dev3.entity_id) + # toggle all lights common.toggle(self.hass) @@ -207,6 +220,32 @@ def test_services(self): light.ATTR_HS_COLOR: (71.059, 100), } == data + # Ensure attributes are filtered when light is turned off + common.turn_on(self.hass, dev1.entity_id, + transition=10, brightness=0, color_name='blue') + common.turn_on( + self.hass, dev2.entity_id, brightness=0, rgb_color=(255, 255, 255), + white_value=0) + common.turn_on(self.hass, dev3.entity_id, brightness=0, + xy_color=(.4, .6)) + + self.hass.block_till_done() + + assert not light.is_on(self.hass, dev1.entity_id) + assert not light.is_on(self.hass, dev2.entity_id) + assert not light.is_on(self.hass, dev3.entity_id) + + _, data = dev1.last_call('turn_off') + assert { + light.ATTR_TRANSITION: 10, + } == data + + _, data = dev2.last_call('turn_off') + assert {} == data + + _, data = dev3.last_call('turn_off') + assert {} == data + # One of the light profiles prof_name, prof_h, prof_s, prof_bri = 'relax', 35.932, 69.412, 144 @@ -292,6 +331,7 @@ def test_light_profiles(self): with open(user_light_file, 'w') as user_file: user_file.write('id,x,y,brightness\n') user_file.write('test,.4,.6,100\n') + user_file.write('test_off,0,0,0\n') assert setup_component( self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}} @@ -305,11 +345,21 @@ def test_light_profiles(self): _, data = dev1.last_call('turn_on') + assert light.is_on(self.hass, dev1.entity_id) assert { light.ATTR_HS_COLOR: (71.059, 100), light.ATTR_BRIGHTNESS: 100 } == data + common.turn_on(self.hass, dev1.entity_id, profile='test_off') + + self.hass.block_till_done() + + _, data = dev1.last_call('turn_off') + + assert not light.is_on(self.hass, dev1.entity_id) + assert {} == data + def test_default_profiles_group(self): """Test default turn-on light profile for all lights.""" platform = loader.get_component(self.hass, 'light.test') diff --git a/tests/components/light/test_tradfri.py b/tests/components/light/test_tradfri.py index 337031cf92c766..37d3ec322ff34a 100644 --- a/tests/components/light/test_tradfri.py +++ b/tests/components/light/test_tradfri.py @@ -35,20 +35,12 @@ 'brightness': 100 } ], - # Brightness == 0 + # Brightness == 1 [ {'can_set_dimmer': True}, - {'brightness': 0}, + {'brightness': 1}, { - 'brightness': 0 - } - ], - # Brightness < 0 - [ - {'can_set_dimmer': True}, - {'brightness': -1}, - { - 'brightness': 0 + 'brightness': 1 } ], # Brightness > 254 From 388d614e30a97d6d2302134d8f600ffe3d01df07 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sat, 30 Mar 2019 21:10:32 -0700 Subject: [PATCH 294/605] Ignore flaky test (#22563) --- tests/components/statistics/test_sensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/components/statistics/test_sensor.py b/tests/components/statistics/test_sensor.py index 580b11f5ccd904..c558c476ca11c4 100644 --- a/tests/components/statistics/test_sensor.py +++ b/tests/components/statistics/test_sensor.py @@ -230,7 +230,7 @@ def mock_now(): state.attributes.get('max_age') assert self.change_rate == state.attributes.get('change_rate') - @pytest.mark.skip + @pytest.mark.skip("Flaky in CI") def test_initialize_from_database(self): """Test initializing the statistics from the database.""" # enable the recorder @@ -260,6 +260,7 @@ def test_initialize_from_database(self): state = self.hass.states.get('sensor.test_mean') assert str(self.mean) == state.state + @pytest.mark.skip("Flaky in CI") def test_initialize_from_database_with_maxage(self): """Test initializing the statistics from the database.""" mock_data = { From 800b1c7fe65fc4938273a16b923fb2ff4ec3f5da Mon Sep 17 00:00:00 2001 From: OleksandrBerchenko Date: Sun, 31 Mar 2019 14:43:54 +0300 Subject: [PATCH 295/605] Fix typo in light/__init__.py (#22581) --- homeassistant/components/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 7fe7ae343f97c1..e3971cd8e420c0 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -465,7 +465,7 @@ def state_attributes(self): if supported_features & SUPPORT_COLOR_TEMP: data[ATTR_COLOR_TEMP] = self.color_temp - if self.supported_features & SUPPORT_COLOR and self.hs_color: + if supported_features & SUPPORT_COLOR and self.hs_color: # pylint: disable=unsubscriptable-object,not-an-iterable hs_color = self.hs_color data[ATTR_HS_COLOR] = ( From 1b0b5b4b8c830165d57cbf9c938ed20138265939 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 31 Mar 2019 16:19:39 +0200 Subject: [PATCH 296/605] Fix lightwave config validation (#22576) --- homeassistant/components/lightwave/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/lightwave/__init__.py b/homeassistant/components/lightwave/__init__.py index a9e5dcf9823c33..f6e11352265dcf 100644 --- a/homeassistant/components/lightwave/__init__.py +++ b/homeassistant/components/lightwave/__init__.py @@ -14,7 +14,7 @@ CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema( - cv.has_at_least_one_key(CONF_LIGHTS, CONF_SWITCHES), { + vol.All(cv.has_at_least_one_key(CONF_LIGHTS, CONF_SWITCHES), { vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_LIGHTS, default={}): { cv.string: vol.Schema({vol.Required(CONF_NAME): cv.string}), @@ -23,6 +23,7 @@ cv.string: vol.Schema({vol.Required(CONF_NAME): cv.string}), } }) + ) }, extra=vol.ALLOW_EXTRA) From 842a36dc9ee5f4a1d27fdc8bb62cdeb8a2d8a9ae Mon Sep 17 00:00:00 2001 From: OleksandrBerchenko Date: Sun, 31 Mar 2019 22:02:45 +0300 Subject: [PATCH 297/605] Rewrite Osram Lightify component (#22184) * Rewrite Osram Lightify component * Update python-lightify version to 1.0.7.2 * Remove unneeded code * 1. Remove changes in light/__init__.py, 2. Set properties to None by default * Fix typo --- .../components/osramlightify/light.py | 376 +++++++++++------- requirements_all.txt | 2 +- 2 files changed, 227 insertions(+), 151 deletions(-) diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index a49e12c76a64ef..59cc2bac5d622a 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -4,41 +4,35 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.osramlightify/ """ -from datetime import timedelta import logging import random import socket import voluptuous as vol -from homeassistant import util from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_HS_COLOR, - ATTR_TRANSITION, EFFECT_RANDOM, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, + ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION, + ATTR_EFFECT, EFFECT_RANDOM, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_TRANSITION, Light) + from homeassistant.const import CONF_HOST import homeassistant.helpers.config_validation as cv -from homeassistant.util.color import ( - color_temperature_kelvin_to_mired, color_temperature_mired_to_kelvin) import homeassistant.util.color as color_util -REQUIREMENTS = ['lightify==1.0.6.1'] +REQUIREMENTS = ['lightify==1.0.7.2'] _LOGGER = logging.getLogger(__name__) CONF_ALLOW_LIGHTIFY_NODES = 'allow_lightify_nodes' CONF_ALLOW_LIGHTIFY_GROUPS = 'allow_lightify_groups' +CONF_INTERVAL_LIGHTIFY_STATUS = 'interval_lightify_status' +CONF_INTERVAL_LIGHTIFY_CONF = 'interval_lightify_conf' DEFAULT_ALLOW_LIGHTIFY_NODES = True DEFAULT_ALLOW_LIGHTIFY_GROUPS = True - -MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) - -SUPPORT_OSRAMLIGHTIFY = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | - SUPPORT_EFFECT | SUPPORT_COLOR | - SUPPORT_TRANSITION) +DEFAULT_INTERVAL_LIGHTIFY_STATUS = 5 +DEFAULT_INTERVAL_LIGHTIFY_CONF = 3600 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, @@ -46,228 +40,310 @@ default=DEFAULT_ALLOW_LIGHTIFY_NODES): cv.boolean, vol.Optional(CONF_ALLOW_LIGHTIFY_GROUPS, default=DEFAULT_ALLOW_LIGHTIFY_GROUPS): cv.boolean, + vol.Optional(CONF_INTERVAL_LIGHTIFY_STATUS, + default=DEFAULT_INTERVAL_LIGHTIFY_STATUS): cv.positive_int, + vol.Optional(CONF_INTERVAL_LIGHTIFY_CONF, + default=DEFAULT_INTERVAL_LIGHTIFY_CONF): cv.positive_int }) +DEFAULT_BRIGHTNESS = 2 +DEFAULT_KELVIN = 2700 + -def setup_platform(hass, config, add_entities, discovery_info=None): +def setup_platform(_hass, config, add_entities, _discovery_info=None): """Set up the Osram Lightify lights.""" import lightify - host = config.get(CONF_HOST) - add_nodes = config.get(CONF_ALLOW_LIGHTIFY_NODES) - add_groups = config.get(CONF_ALLOW_LIGHTIFY_GROUPS) - + host = config[CONF_HOST] try: - bridge = lightify.Lightify(host) + bridge = lightify.Lightify(host, log_level=logging.NOTSET) except socket.error as err: msg = "Error connecting to bridge: {} due to: {}".format( host, str(err)) _LOGGER.exception(msg) return - setup_bridge(bridge, add_entities, add_nodes, add_groups) + setup_bridge(bridge, add_entities, config) -def setup_bridge(bridge, add_entities, add_nodes, add_groups): +def setup_bridge(bridge, add_entities, config): """Set up the Lightify bridge.""" lights = {} + groups = {} + groups_last_updated = [0] - @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): - """Update the lights objects with latest info from bridge.""" + """Update the lights objects with the latest info from the bridge.""" try: - bridge.update_all_light_status() - bridge.update_group_list() + new_lights = bridge.update_all_light_status( + config[CONF_INTERVAL_LIGHTIFY_STATUS]) + lights_changed = bridge.lights_changed() except TimeoutError: _LOGGER.error("Timeout during updating of lights") + return 0 except OSError: _LOGGER.error("OSError during updating of lights") + return 0 + + if new_lights and config[CONF_ALLOW_LIGHTIFY_NODES]: + new_entities = [] + for addr, light in new_lights.items(): + if addr not in lights: + osram_light = OsramLightifyLight(light, update_lights, + lights_changed) + lights[addr] = osram_light + new_entities.append(osram_light) + else: + lights[addr].update_luminary(light) - new_lights = [] + add_entities(new_entities) - if add_nodes: - for (light_id, light) in bridge.lights().items(): - if light_id not in lights: - osram_light = OsramLightifyLight( - light_id, light, update_lights) - lights[light_id] = osram_light - new_lights.append(osram_light) - else: - lights[light_id].light = light - - if add_groups: - for (group_name, group) in bridge.groups().items(): - if group_name not in lights: - osram_group = OsramLightifyGroup( - group, bridge, update_lights) - lights[group_name] = osram_group - new_lights.append(osram_group) + return lights_changed + + def update_groups(): + """Update the groups objects with the latest info from the bridge.""" + lights_changed = update_lights() + + try: + new_groups = bridge.update_group_list( + config[CONF_INTERVAL_LIGHTIFY_CONF]) + groups_updated = bridge.groups_updated() + except TimeoutError: + _LOGGER.error("Timeout during updating of groups") + return 0 + except OSError: + _LOGGER.error("OSError during updating of groups") + return 0 + + if new_groups: + new_groups = {group.idx(): group for group in new_groups.values()} + new_entities = [] + for idx, group in new_groups.items(): + if idx not in groups: + osram_group = OsramLightifyGroup(group, update_groups, + groups_updated) + groups[idx] = osram_group + new_entities.append(osram_group) else: - lights[group_name].group = group + groups[idx].update_luminary(group) - if new_lights: - add_entities(new_lights) + add_entities(new_entities) + + if groups_updated > groups_last_updated[0]: + groups_last_updated[0] = groups_updated + for idx, osram_group in groups.items(): + if idx not in new_groups: + osram_group.update_static_attributes() + + return max(lights_changed, groups_updated) update_lights() + if config[CONF_ALLOW_LIGHTIFY_GROUPS]: + update_groups() class Luminary(Light): """Representation of Luminary Lights and Groups.""" - def __init__(self, luminary, update_lights): - """Initialize a Luminary light.""" - self.update_lights = update_lights + def __init__(self, luminary, update_func, changed): + """Initialize a Luminary Light.""" + self.update_func = update_func self._luminary = luminary + self._changed = changed + + self._unique_id = None + self._supported_features = [] + self._effect_list = [] + self._is_on = False + self._min_mireds = None + self._max_mireds = None self._brightness = None - self._hs = None - self._name = None - self._temperature = None - self._state = False - self.update() + self._color_temp = None + self._rgb_color = None + + self.update_static_attributes() + self.update_dynamic_attributes() + + def _get_unique_id(self): + """Get a unique ID (not implemented).""" + raise NotImplementedError + + def _get_supported_features(self): + """Get list of supported features.""" + features = 0 + if 'lum' in self._luminary.supported_features(): + features = features | SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + + if 'temp' in self._luminary.supported_features(): + features = features | SUPPORT_COLOR_TEMP | SUPPORT_TRANSITION + + if 'rgb' in self._luminary.supported_features(): + features = (features | SUPPORT_COLOR | SUPPORT_TRANSITION | + SUPPORT_EFFECT) + + return features + + def _get_effect_list(self): + """Get list of supported effects.""" + effects = [] + if 'rgb' in self._luminary.supported_features(): + effects.append(EFFECT_RANDOM) + + return effects @property def name(self): - """Return the name of the device if any.""" - return self._name + """Return the name of the luminary.""" + return self._luminary.name() @property def hs_color(self): - """Last hs color value set.""" - return self._hs + """Return last hs color value set.""" + return color_util.color_RGB_to_hs(*self._rgb_color) @property def color_temp(self): """Return the color temperature.""" - return self._temperature + return self._color_temp @property def brightness(self): - """Brightness of this light between 0..255.""" + """Return brightness of the luminary (0..255).""" return self._brightness @property def is_on(self): - """Update status to True if device is on.""" - return self._state + """Return True if the device is on.""" + return self._is_on @property def supported_features(self): - """Flag supported features.""" - return SUPPORT_OSRAMLIGHTIFY + """List of supported features.""" + return self._supported_features @property def effect_list(self): """List of supported effects.""" - return [EFFECT_RANDOM] + return self._effect_list + + @property + def min_mireds(self): + """Return the coldest color_temp that this light supports.""" + return self._min_mireds + + @property + def max_mireds(self): + """Return the warmest color_temp that this light supports.""" + return self._max_mireds + + @property + def unique_id(self): + """Return a unique ID.""" + return self._unique_id + + def play_effect(self, effect, transition): + """Play selected effect.""" + if effect == EFFECT_RANDOM: + self._rgb_color = (random.randrange(0, 256), + random.randrange(0, 256), + random.randrange(0, 256)) + self._luminary.set_rgb(*self._rgb_color, transition) + self._luminary.set_onoff(True) + return True + + return False def turn_on(self, **kwargs): """Turn the device on.""" - if ATTR_TRANSITION in kwargs: - transition = int(kwargs[ATTR_TRANSITION] * 10) - else: - transition = 0 - - if ATTR_BRIGHTNESS in kwargs: - self._brightness = kwargs[ATTR_BRIGHTNESS] - self._luminary.set_luminance( - int(self._brightness / 2.55), transition) - else: - self._luminary.set_onoff(1) + transition = int(kwargs.get(ATTR_TRANSITION, 0) * 10) + if ATTR_EFFECT in kwargs: + self.play_effect(kwargs[ATTR_EFFECT], transition) + return if ATTR_HS_COLOR in kwargs: - red, green, blue = \ - color_util.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR]) - self._luminary.set_rgb(red, green, blue, transition) + self._rgb_color = color_util.color_hs_to_RGB( + *kwargs[ATTR_HS_COLOR]) + self._luminary.set_rgb(*self._rgb_color, transition) if ATTR_COLOR_TEMP in kwargs: - color_t = kwargs[ATTR_COLOR_TEMP] - kelvin = int(color_temperature_mired_to_kelvin(color_t)) - self._luminary.set_temperature(kelvin, transition) - - if ATTR_EFFECT in kwargs: - effect = kwargs.get(ATTR_EFFECT) - if effect == EFFECT_RANDOM: - self._luminary.set_rgb( - random.randrange(0, 255), random.randrange(0, 255), - random.randrange(0, 255), transition) + self._color_temp = kwargs[ATTR_COLOR_TEMP] + self._luminary.set_temperature( + int(color_util.color_temperature_mired_to_kelvin( + self._color_temp)), transition) - self.schedule_update_ha_state() + self._is_on = True + if ATTR_BRIGHTNESS in kwargs: + self._brightness = kwargs[ATTR_BRIGHTNESS] + self._luminary.set_luminance(int(self._brightness / 2.55), + transition) + else: + self._luminary.set_onoff(True) def turn_off(self, **kwargs): """Turn the device off.""" + self._is_on = False if ATTR_TRANSITION in kwargs: transition = int(kwargs[ATTR_TRANSITION] * 10) + self._brightness = DEFAULT_BRIGHTNESS self._luminary.set_luminance(0, transition) else: - transition = 0 - self._luminary.set_onoff(0) - self.schedule_update_ha_state() + self._luminary.set_onoff(False) + + def update_luminary(self, luminary): + """Update internal luminary object.""" + self._luminary = luminary + self.update_static_attributes() + + def update_static_attributes(self): + """Update static attributes of the luminary.""" + self._unique_id = self._get_unique_id() + self._supported_features = self._get_supported_features() + self._effect_list = self._get_effect_list() + if self._supported_features & SUPPORT_COLOR_TEMP: + self._min_mireds = color_util.color_temperature_kelvin_to_mired( + self._luminary.max_temp() or DEFAULT_KELVIN) + self._max_mireds = color_util.color_temperature_kelvin_to_mired( + self._luminary.min_temp() or DEFAULT_KELVIN) + + def update_dynamic_attributes(self): + """Update dynamic attributes of the luminary.""" + self._is_on = self._luminary.on() + if self._supported_features & SUPPORT_BRIGHTNESS: + self._brightness = int(self._luminary.lum() * 2.55) + + if self._supported_features & SUPPORT_COLOR_TEMP: + self._color_temp = color_util.color_temperature_kelvin_to_mired( + self._luminary.temp() or DEFAULT_KELVIN) + + if self._supported_features & SUPPORT_COLOR: + self._rgb_color = self._luminary.rgb() def update(self): """Synchronize state with bridge.""" - self.update_lights(no_throttle=True) - self._name = self._luminary.name() + changed = self.update_func() + if changed > self._changed: + self._changed = changed + self.update_dynamic_attributes() class OsramLightifyLight(Luminary): """Representation of an Osram Lightify Light.""" - def __init__(self, light_id, light, update_lights): - """Initialize the Lightify light.""" - self._light_id = light_id - super().__init__(light, update_lights) - - def update(self): - """Update status of a light.""" - super().update() - self._state = self._luminary.on() - rgb = self._luminary.rgb() - self._hs = color_util.color_RGB_to_hs(*rgb) - o_temp = self._luminary.temp() - if o_temp == 0: - self._temperature = None - else: - self._temperature = color_temperature_kelvin_to_mired( - self._luminary.temp()) - self._brightness = int(self._luminary.lum() * 2.55) - - @property - def unique_id(self): - """Return a unique ID.""" - return self._light_id + def _get_unique_id(self): + """Get a unique ID.""" + return self._luminary.addr() class OsramLightifyGroup(Luminary): """Representation of an Osram Lightify Group.""" - def __init__(self, group, bridge, update_lights): - """Initialize the Lightify light group.""" - self._bridge = bridge - self._light_ids = [] - super().__init__(group, update_lights) - self._unique_id = '{}'.format(self._light_ids) - - def _get_state(self): - """Get state of group.""" - lights = self._bridge.lights() - return any(lights[light_id].on() for light_id in self._light_ids) - - def update(self): - """Update group status.""" - super().update() - self._light_ids = self._luminary.lights() - light = self._bridge.lights()[self._light_ids[0]] - self._brightness = int(light.lum() * 2.55) - rgb = light.rgb() - self._hs = color_util.color_RGB_to_hs(*rgb) - o_temp = light.temp() - if o_temp == 0: - self._temperature = None - else: - self._temperature = color_temperature_kelvin_to_mired(o_temp) - self._state = light.on() - - @property - def unique_id(self): - """Return a unique ID.""" - return self._unique_id + def _get_unique_id(self): + """Get a unique ID for the group.""" +# Actually, it's a wrong choice for a unique ID, because a combination of +# lights is NOT unique (Osram Lightify allows to create different groups +# with the same lights). Also a combination of lights may easily change, +# but the group remains the same from the user's perspective. +# It should be something like "-" +# For now keeping it as is for backward compatibility with existing +# users. + return '{}'.format(self._luminary.lights()) diff --git a/requirements_all.txt b/requirements_all.txt index ec4c602835635a..18f9098a2e47f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -643,7 +643,7 @@ libsoundtouch==0.7.2 liffylights==0.9.4 # homeassistant.components.osramlightify.light -lightify==1.0.6.1 +lightify==1.0.7.2 # homeassistant.components.lightwave lightwave==0.15 From 5abfc8438280c85eeeddc93819cf2488aa6d5967 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Sun, 31 Mar 2019 21:18:45 +0200 Subject: [PATCH 298/605] Clean up homematicip cloud (#22589) * Code Cleanup - removed unused constants - more icons on binary_sensor groups - alligned code for device_state_attributes - fixed temperature unit origin for weather * removed icons --- .../homematicip_cloud/alarm_control_panel.py | 3 --- .../components/homematicip_cloud/climate.py | 2 -- .../components/homematicip_cloud/light.py | 16 ++++------------ .../components/homematicip_cloud/sensor.py | 2 -- .../components/homematicip_cloud/switch.py | 4 ---- .../components/homematicip_cloud/weather.py | 3 ++- 6 files changed, 6 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index eb5855bb98099e..df0201340ed127 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -12,9 +12,6 @@ DEPENDENCIES = ['homematicip_cloud'] -HMIP_ZONE_AWAY = 'EXTERNAL' -HMIP_ZONE_HOME = 'INTERNAL' - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index 955f3e5baa7342..5055858e9c78d1 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -STATE_BOOST = 'Boost' - HA_STATE_TO_HMIP = { STATE_AUTO: 'AUTOMATIC', STATE_MANUAL: 'MANUAL', diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index f8b19b5bb1edbc..f5bac66388c6d5 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -13,7 +13,6 @@ ATTR_ENERGY_COUNTER = 'energy_counter_kwh' ATTR_POWER_CONSUMPTION = 'power_consumption' -ATTR_PROFILE_MODE = 'profile_mode' async def async_setup_platform( @@ -77,13 +76,9 @@ def device_state_attributes(self): """Return the state attributes of the generic device.""" attr = super().device_state_attributes if self._device.currentPowerConsumption > 0.05: - attr.update({ - ATTR_POWER_CONSUMPTION: - round(self._device.currentPowerConsumption, 2) - }) - attr.update({ - ATTR_ENERGY_COUNTER: round(self._device.energyCounter, 2) - }) + attr[ATTR_POWER_CONSUMPTION] = \ + round(self._device.currentPowerConsumption, 2) + attr[ATTR_ENERGY_COUNTER] = round(self._device.energyCounter, 2) return attr @@ -168,10 +163,7 @@ def device_state_attributes(self): """Return the state attributes of the generic device.""" attr = super().device_state_attributes if self.is_on: - attr.update({ - ATTR_COLOR_NAME: - self._channel.simpleRGBColorState - }) + attr[ATTR_COLOR_NAME] = self._channel.simpleRGBColorState return attr @property diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 39758739400291..e053c191c6baae 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -12,8 +12,6 @@ DEPENDENCIES = ['homematicip_cloud'] ATTR_TEMPERATURE_OFFSET = 'temperature_offset' -ATTR_VALVE_STATE = 'valve_state' -ATTR_VALVE_POSITION = 'valve_position' ATTR_WIND_DIRECTION = 'wind_direction' ATTR_WIND_DIRECTION_VARIATION = 'wind_direction_variation_in_degree' diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index 62e72f0ade7b5a..2199b867002b57 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -10,10 +10,6 @@ _LOGGER = logging.getLogger(__name__) -ATTR_POWER_CONSUMPTION = 'power_consumption' -ATTR_ENERGIE_COUNTER = 'energie_counter' -ATTR_PROFILE_MODE = 'profile_mode' - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index 101adcdeaaa478..ba3157471f9c80 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -3,6 +3,7 @@ import logging from homeassistant.components.weather import WeatherEntity +from homeassistant.const import TEMP_CELSIUS from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice @@ -55,7 +56,7 @@ def temperature(self): @property def temperature_unit(self): """Return the unit of measurement.""" - return self.hass.config.units.temperature_unit + return TEMP_CELSIUS @property def humidity(self): From 7d7b931163636d1b778b64e52cfed60b85f76087 Mon Sep 17 00:00:00 2001 From: Sander Cornelissen Date: Sun, 31 Mar 2019 22:00:48 +0200 Subject: [PATCH 299/605] Retrying connecting Influxdb at setup (#22567) * Also retry Influxdb at setup() * Use event.call_later() for retry setup Influxdb * Fix max line length in setup() in Influxdb * Add extra space before comment * Fix sec -> seconds and add return True --- homeassistant/components/influxdb/__init__.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/influxdb/__init__.py b/homeassistant/components/influxdb/__init__.py index b421960b51fbce..551996983c8004 100644 --- a/homeassistant/components/influxdb/__init__.py +++ b/homeassistant/components/influxdb/__init__.py @@ -14,7 +14,7 @@ CONF_PASSWORD, CONF_PORT, CONF_SSL, CONF_USERNAME, CONF_VERIFY_SSL, EVENT_STATE_CHANGED, EVENT_HOMEASSISTANT_STOP, STATE_UNAVAILABLE, STATE_UNKNOWN) -from homeassistant.helpers import state as state_helper +from homeassistant.helpers import state as state_helper, event as event_helper import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_values import EntityValues @@ -39,6 +39,7 @@ TIMEOUT = 5 RETRY_DELAY = 20 QUEUE_BACKLOG_SECONDS = 30 +RETRY_INTERVAL = 60 # seconds BATCH_TIMEOUT = 1 BATCH_BUFFER_SIZE = 100 @@ -134,11 +135,16 @@ def setup(hass, config): influx.write_points([]) except (exceptions.InfluxDBClientError, requests.exceptions.ConnectionError) as exc: - _LOGGER.error("Database host is not accessible due to '%s', please " - "check your entries in the configuration file (host, " - "port, etc.) and verify that the database exists and is " - "READ/WRITE", exc) - return False + _LOGGER.warning( + "Database host is not accessible due to '%s', please " + "check your entries in the configuration file (host, " + "port, etc.) and verify that the database exists and is " + "READ/WRITE. Retrying again in %s seconds.", exc, RETRY_INTERVAL + ) + event_helper.call_later( + hass, RETRY_INTERVAL, lambda _: setup(hass, config) + ) + return True def event_to_json(event): """Add an event to the outgoing Influx list.""" From 755571abe3946a58a48832dd1d7c63db03df9acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Marc=20Simard?= Date: Sun, 31 Mar 2019 17:21:45 -0400 Subject: [PATCH 300/605] Fix gtfs typing and logger issues (#22572) ## Description: Some code cleanup requests where raised in the [latest merged GTFS commit](https://github.com/home-assistant/home-assistant/pull/20966/commits/9153e3b671990d3c33f59b8cde5506b30fcaaa65). This new PR aims to address them, including: - Clear all typing issues. - Respect logger levels and format. - Simplify some non-pythonic lines. This sensor now passes `mypy` testing, but does so by ignoring two lines with `# type: ignore`. **Related issue (if applicable):** fixes issues raised by @MartinHjelmare in #20966 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- homeassistant/components/gtfs/sensor.py | 70 ++++++++++++------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 5555459aa16bb3..85f2651d1f6159 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -130,7 +130,7 @@ def get_next_departure(schedule: Any, start_station_id: Any, end_station_id: Any, offset: cv.time_period, - include_tomorrow: cv.boolean = False) -> dict: + include_tomorrow: bool = False) -> dict: """Get the next departure for the given schedule.""" now = datetime.datetime.now() + offset now_date = now.strftime(dt_util.DATE_STR_FORMAT) @@ -147,7 +147,7 @@ def get_next_departure(schedule: Any, start_station_id: Any, limit = 24 * 60 * 60 * 2 tomorrow_select = tomorrow_where = tomorrow_order = '' if include_tomorrow: - limit = limit / 2 * 3 + limit = int(limit / 2 * 3) tomorrow_name = tomorrow.strftime('%A').lower() tomorrow_select = "calendar.{} AS tomorrow,".format(tomorrow_name) tomorrow_where = "OR calendar.{} = 1".format(tomorrow_name) @@ -218,7 +218,7 @@ def get_next_departure(schedule: Any, start_station_id: Any, # as long as all departures are within the calendar date range. timetable = {} yesterday_start = today_start = tomorrow_start = None - yesterday_last = today_last = None + yesterday_last = today_last = '' for row in result: if row['yesterday'] == 1 and yesterday_date >= row['start_date']: @@ -274,7 +274,7 @@ def get_next_departure(schedule: Any, start_station_id: Any, _LOGGER.debug("Timetable: %s", sorted(timetable.keys())) - item = {} + item = {} # type: dict for key in sorted(timetable.keys()): if dt_util.parse_datetime(key) > now: item = timetable[key] @@ -350,22 +350,22 @@ def get_next_departure(schedule: Any, start_station_id: Any, def setup_platform(hass: HomeAssistantType, config: ConfigType, add_entities: Callable[[list], None], - discovery_info: Optional[dict] = None) -> bool: + discovery_info: Optional[dict] = None) -> None: """Set up the GTFS sensor.""" gtfs_dir = hass.config.path(DEFAULT_PATH) - data = str(config.get(CONF_DATA)) + data = config[CONF_DATA] origin = config.get(CONF_ORIGIN) destination = config.get(CONF_DESTINATION) name = config.get(CONF_NAME) offset = config.get(CONF_OFFSET) - include_tomorrow = config.get(CONF_TOMORROW) + include_tomorrow = config[CONF_TOMORROW] if not os.path.exists(gtfs_dir): os.makedirs(gtfs_dir) if not os.path.exists(os.path.join(gtfs_dir, data)): _LOGGER.error("The given GTFS data file/folder was not found") - return False + return import pygtfs @@ -382,7 +382,6 @@ def setup_platform(hass: HomeAssistantType, config: ConfigType, add_entities([ GTFSDepartureSensor(gtfs, name, origin, destination, offset, include_tomorrow)]) - return True class GTFSDepartureSensor(Entity): @@ -390,7 +389,7 @@ class GTFSDepartureSensor(Entity): def __init__(self, pygtfs: Any, name: Optional[Any], origin: Any, destination: Any, offset: cv.time_period, - include_tomorrow: cv.boolean) -> None: + include_tomorrow: bool) -> None: """Initialize the sensor.""" self._pygtfs = pygtfs self.origin = origin @@ -402,7 +401,7 @@ def __init__(self, pygtfs: Any, name: Optional[Any], origin: Any, self._available = False self._icon = ICON self._name = '' - self._state = None + self._state = None # type: Optional[str] self._attributes = {} # type: dict self._agency = None @@ -421,10 +420,8 @@ def name(self) -> str: return self._name @property - def state(self) -> str: + def state(self) -> Optional[str]: # type: ignore """Return the state of the sensor.""" - if self._state is None: - return STATE_UNKNOWN return self._state @property @@ -488,26 +485,27 @@ def update(self) -> None: else: trip_id = self._departure['trip_id'] if not self._trip or self._trip.trip_id != trip_id: - _LOGGER.info("Fetching trip details for %s", trip_id) + _LOGGER.debug("Fetching trip details for %s", trip_id) self._trip = self._pygtfs.trips_by_id(trip_id)[0] route_id = self._departure['route_id'] if not self._route or self._route.route_id != route_id: - _LOGGER.info("Fetching route details for %s", route_id) + _LOGGER.debug("Fetching route details for %s", route_id) self._route = self._pygtfs.routes_by_id(route_id)[0] # Fetch agency details exactly once if self._agency is None and self._route: + _LOGGER.debug("Fetching agency details for %s", + self._route.agency_id) try: - _LOGGER.info("Fetching agency details for %s", - self._route.agency_id) self._agency = self._pygtfs.agencies_by_id( self._route.agency_id)[0] except IndexError: _LOGGER.warning( - "Agency ID '%s' not found in agency table. You may " - "want to update the agency database table to fix this " - "missing reference.", self._route.agency_id) + "Agency ID '%s' was not found in agency table, " + "you may want to update the routes database table " + "to fix this missing reference", + self._route.agency_id) self._agency = False # Assign attributes, icon and name @@ -540,21 +538,21 @@ def update_attributes(self) -> None: if self._departure[ATTR_FIRST] is not None: self._attributes[ATTR_FIRST] = self._departure['first'] - elif ATTR_FIRST in self._attributes.keys(): + elif ATTR_FIRST in self._attributes: del self._attributes[ATTR_FIRST] if self._departure[ATTR_LAST] is not None: self._attributes[ATTR_LAST] = self._departure['last'] - elif ATTR_LAST in self._attributes.keys(): + elif ATTR_LAST in self._attributes: del self._attributes[ATTR_LAST] else: - if ATTR_ARRIVAL in self._attributes.keys(): + if ATTR_ARRIVAL in self._attributes: del self._attributes[ATTR_ARRIVAL] - if ATTR_DAY in self._attributes.keys(): + if ATTR_DAY in self._attributes: del self._attributes[ATTR_DAY] - if ATTR_FIRST in self._attributes.keys(): + if ATTR_FIRST in self._attributes: del self._attributes[ATTR_FIRST] - if ATTR_LAST in self._attributes.keys(): + if ATTR_LAST in self._attributes: del self._attributes[ATTR_LAST] # Add contextual information @@ -563,21 +561,21 @@ def update_attributes(self) -> None: if self._state is None: self._attributes[ATTR_INFO] = "No more departures" if \ self._include_tomorrow else "No more departures today" - elif ATTR_INFO in self._attributes.keys(): + elif ATTR_INFO in self._attributes: del self._attributes[ATTR_INFO] if self._agency: self._attributes[ATTR_ATTRIBUTION] = self._agency.agency_name - elif ATTR_ATTRIBUTION in self._attributes.keys(): + elif ATTR_ATTRIBUTION in self._attributes: del self._attributes[ATTR_ATTRIBUTION] # Add extra metadata key = 'agency_id' - if self._agency and key not in self._attributes.keys(): + if self._agency and key not in self._attributes: self.append_keys(self.dict_for_table(self._agency), 'Agency') key = 'origin_station_stop_id' - if self._origin and key not in self._attributes.keys(): + if self._origin and key not in self._attributes: self.append_keys(self.dict_for_table(self._origin), "Origin Station") self._attributes[ATTR_LOCATION_ORIGIN] = \ @@ -590,7 +588,7 @@ def update_attributes(self) -> None: WHEELCHAIR_BOARDING_DEFAULT) key = 'destination_station_stop_id' - if self._destination and key not in self._attributes.keys(): + if self._destination and key not in self._attributes: self.append_keys(self.dict_for_table(self._destination), "Destination Station") self._attributes[ATTR_LOCATION_DESTINATION] = \ @@ -604,9 +602,9 @@ def update_attributes(self) -> None: # Manage Route metadata key = 'route_id' - if not self._route and key in self._attributes.keys(): + if not self._route and key in self._attributes: self.remove_keys('Route') - elif self._route and (key not in self._attributes.keys() or + elif self._route and (key not in self._attributes or self._attributes[key] != self._route.route_id): self.append_keys(self.dict_for_table(self._route), 'Route') self._attributes[ATTR_ROUTE_TYPE] = \ @@ -614,9 +612,9 @@ def update_attributes(self) -> None: # Manage Trip metadata key = 'trip_id' - if not self._trip and key in self._attributes.keys(): + if not self._trip and key in self._attributes: self.remove_keys('Trip') - elif self._trip and (key not in self._attributes.keys() or + elif self._trip and (key not in self._attributes or self._attributes[key] != self._trip.trip_id): self.append_keys(self.dict_for_table(self._trip), 'Trip') self._attributes[ATTR_BICYCLE] = BICYCLE_ALLOWED_OPTIONS.get( From e085383d2dc5fd57c502cbf6b0c02bfad8271967 Mon Sep 17 00:00:00 2001 From: drjared88 Date: Sun, 31 Mar 2019 16:12:55 -0600 Subject: [PATCH 301/605] Update ONVIF component to SUPPORT_STREAM (#22569) * Update Onvif component to SUPPORT_STREAM * Update camera.py * Update camera.py * Update camera.py Remove extra spaces. * lookup URL when camera is added to hass and add extra guards --- homeassistant/components/onvif/camera.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 36f1b18eebf0e7..09d47c3c0c9761 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -13,7 +13,8 @@ from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, ATTR_ENTITY_ID) -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA +from homeassistant.components.camera import ( + Camera, PLATFORM_SCHEMA, SUPPORT_STREAM) from homeassistant.components.camera.const import DOMAIN from homeassistant.components.ffmpeg import ( DATA_FFMPEG, CONF_EXTRA_ARGUMENTS) @@ -187,13 +188,14 @@ async def async_added_to_hass(self): self.hass.data[ONVIF_DATA] = {} self.hass.data[ONVIF_DATA][ENTITIES] = [] self.hass.data[ONVIF_DATA][ENTITIES].append(self) + await self.hass.async_add_executor_job(self.obtain_input_uri) async def async_camera_image(self): """Return a still image response from the camera.""" from haffmpeg.tools import ImageFrame, IMAGE_JPEG if not self._input: - await self.hass.async_add_job(self.obtain_input_uri) + await self.hass.async_add_executor_job(self.obtain_input_uri) if not self._input: return None @@ -210,7 +212,7 @@ async def handle_async_mjpeg_stream(self, request): from haffmpeg.camera import CameraMjpeg if not self._input: - await self.hass.async_add_job(self.obtain_input_uri) + await self.hass.async_add_executor_job(self.obtain_input_uri) if not self._input: return None @@ -228,6 +230,18 @@ async def handle_async_mjpeg_stream(self, request): finally: await stream.close() + @property + def supported_features(self): + """Return supported features.""" + if self._input: + return SUPPORT_STREAM + return 0 + + @property + def stream_source(self): + """Return the stream source.""" + return self._input + @property def name(self): """Return the name of this camera.""" From 50a0504e07c971874a1bedf99ebd17a5383c88b1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 17:14:19 -0700 Subject: [PATCH 302/605] Add stream to the default config (#22602) --- homeassistant/components/default_config/__init__.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/default_config/__init__.py b/homeassistant/components/default_config/__init__.py index 888a4d51c956f7..6743893888d819 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -1,7 +1,11 @@ """Component providing default configuration for new users.""" +try: + import av +except ImportError: + av = None DOMAIN = 'default_config' -DEPENDENCIES = ( +DEPENDENCIES = [ 'automation', 'cloud', 'config', @@ -17,7 +21,10 @@ 'system_health', 'updater', 'zeroconf', -) +] +# Only automatically set up the stream component when dependency installed +if av is not None: + DEPENDENCIES.append('stream') async def async_setup(hass, config): From 3d8efd42006a5a25ccc3393d843d7de30ecbce98 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 31 Mar 2019 20:32:55 -0600 Subject: [PATCH 303/605] Add permission checking to all RainMachine services (#22399) * Add permission checking to all RainMachine services * Linting * Some initial work * Owner comments * Test in place (I think) * Linting * Update conftest.py --- .../components/rainmachine/__init__.py | 94 ++++++++++++++----- tests/components/rainmachine/conftest.py | 23 +++++ .../rainmachine/test_service_permissions.py | 41 ++++++++ 3 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 tests/components/rainmachine/conftest.py create mode 100644 tests/components/rainmachine/test_service_permissions.py diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 6d986fa5c678a0..2ff5ddcd4aa2ea 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -1,15 +1,18 @@ """Support for RainMachine devices.""" import logging from datetime import timedelta +from functools import wraps import voluptuous as vol +from homeassistant.auth.permissions.const import POLICY_CONTROL from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_BINARY_SENSORS, CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, CONF_SENSORS, CONF_SSL, CONF_MONITORED_CONDITIONS, CONF_SWITCHES) -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ( + ConfigEntryNotReady, Unauthorized, UnknownUser) from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity @@ -128,6 +131,44 @@ }, extra=vol.ALLOW_EXTRA) +def _check_valid_user(hass): + """Ensure the user of a service call has proper permissions.""" + def decorator(service): + """Decorate.""" + @wraps(service) + async def check_permissions(call): + """Check user permission and raise before call if unauthorized.""" + if not call.context.user_id: + return + + user = await hass.auth.async_get_user(call.context.user_id) + if user is None: + raise UnknownUser( + context=call.context, + permission=POLICY_CONTROL + ) + + # RainMachine services don't interact with specific entities. + # Therefore, we examine _all_ RainMachine entities and if the user + # has permission to control _any_ of them, the user has permission + # to call the service: + en_reg = await hass.helpers.entity_registry.async_get_registry() + rainmachine_entities = [ + entity.entity_id for entity in en_reg.entities.values() + if entity.platform == DOMAIN + ] + for entity_id in rainmachine_entities: + if user.permissions.check_entity(entity_id, POLICY_CONTROL): + return await service(call) + + raise Unauthorized( + context=call.context, + permission=POLICY_CONTROL, + ) + return check_permissions + return decorator + + async def async_setup(hass, config): """Set up the RainMachine component.""" hass.data[DOMAIN] = {} @@ -197,59 +238,70 @@ async def refresh(event_time): refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])) - async def disable_program(service): + @_check_valid_user(hass) + async def disable_program(call): """Disable a program.""" await rainmachine.client.programs.disable( - service.data[CONF_PROGRAM_ID]) + call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def disable_zone(service): + @_check_valid_user(hass) + async def disable_zone(call): """Disable a zone.""" - await rainmachine.client.zones.disable(service.data[CONF_ZONE_ID]) + await rainmachine.client.zones.disable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - async def enable_program(service): + @_check_valid_user(hass) + async def enable_program(call): """Enable a program.""" - await rainmachine.client.programs.enable(service.data[CONF_PROGRAM_ID]) + await rainmachine.client.programs.enable(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def enable_zone(service): + @_check_valid_user(hass) + async def enable_zone(call): """Enable a zone.""" - await rainmachine.client.zones.enable(service.data[CONF_ZONE_ID]) + await rainmachine.client.zones.enable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - async def pause_watering(service): + @_check_valid_user(hass) + async def pause_watering(call): """Pause watering for a set number of seconds.""" - await rainmachine.client.watering.pause_all(service.data[CONF_SECONDS]) + await rainmachine.client.watering.pause_all(call.data[CONF_SECONDS]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def start_program(service): + @_check_valid_user(hass) + async def start_program(call): """Start a particular program.""" - await rainmachine.client.programs.start(service.data[CONF_PROGRAM_ID]) + await rainmachine.client.programs.start(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def start_zone(service): + @_check_valid_user(hass) + async def start_zone(call): """Start a particular zone for a certain amount of time.""" await rainmachine.client.zones.start( - service.data[CONF_ZONE_ID], service.data[CONF_ZONE_RUN_TIME]) + call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - async def stop_all(service): + @_check_valid_user(hass) + async def stop_all(call): """Stop all watering.""" await rainmachine.client.watering.stop_all() async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def stop_program(service): + @_check_valid_user(hass) + async def stop_program(call): """Stop a program.""" - await rainmachine.client.programs.stop(service.data[CONF_PROGRAM_ID]) + await rainmachine.client.programs.stop(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - async def stop_zone(service): + @_check_valid_user(hass) + async def stop_zone(call): """Stop a zone.""" - await rainmachine.client.zones.stop(service.data[CONF_ZONE_ID]) + await rainmachine.client.zones.stop(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - async def unpause_watering(service): + @_check_valid_user(hass) + async def unpause_watering(call): """Unpause watering.""" await rainmachine.client.watering.unpause_all() async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) diff --git a/tests/components/rainmachine/conftest.py b/tests/components/rainmachine/conftest.py new file mode 100644 index 00000000000000..fdc81151995601 --- /dev/null +++ b/tests/components/rainmachine/conftest.py @@ -0,0 +1,23 @@ +"""Configuration for Rainmachine tests.""" +import pytest + +from homeassistant.components.rainmachine.const import DOMAIN +from homeassistant.const import ( + CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, CONF_SSL) + +from tests.common import MockConfigEntry + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(): + """Create a mock RainMachine config entry.""" + return MockConfigEntry( + domain=DOMAIN, + title='192.168.1.101', + data={ + CONF_IP_ADDRESS: '192.168.1.101', + CONF_PASSWORD: '12345', + CONF_PORT: 8080, + CONF_SSL: True, + CONF_SCAN_INTERVAL: 60, + }) diff --git a/tests/components/rainmachine/test_service_permissions.py b/tests/components/rainmachine/test_service_permissions.py new file mode 100644 index 00000000000000..caa84337517c00 --- /dev/null +++ b/tests/components/rainmachine/test_service_permissions.py @@ -0,0 +1,41 @@ +"""Define tests for permissions on RainMachine service calls.""" +import asynctest +import pytest + +from homeassistant.components.rainmachine.const import DOMAIN +from homeassistant.core import Context +from homeassistant.exceptions import Unauthorized, UnknownUser +from homeassistant.setup import async_setup_component + +from tests.common import mock_coro + + +async def setup_platform(hass, config_entry): + """Set up the media player platform for testing.""" + with asynctest.mock.patch('regenmaschine.login') as mock_login: + mock_client = mock_login.return_value + mock_client.restrictions.current.return_value = mock_coro() + mock_client.restrictions.universal.return_value = mock_coro() + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN) + await hass.async_block_till_done() + + +async def test_services_authorization( + hass, config_entry, hass_read_only_user): + """Test that a RainMachine service is halted on incorrect permissions.""" + await setup_platform(hass, config_entry) + + with pytest.raises(UnknownUser): + await hass.services.async_call( + 'rainmachine', + 'unpause_watering', {}, + blocking=True, + context=Context(user_id='fake_user_id')) + + with pytest.raises(Unauthorized): + await hass.services.async_call( + 'rainmachine', + 'unpause_watering', {}, + blocking=True, + context=Context(user_id=hass_read_only_user.id)) From 9a4b0cfb9b51400980ee5b797f3301e4861ac8dd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 19:52:44 -0700 Subject: [PATCH 304/605] Updated frontend to 20190331.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 3baea2008b144b..f0358dbd6cc8d6 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190329.0'] +REQUIREMENTS = ['home-assistant-frontend==20190331.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 18f9098a2e47f8..e3cc4a349541c8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -551,7 +551,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190329.0 +home-assistant-frontend==20190331.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6c7f5b6a5a7b55..85b49f71ce70ea 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -129,7 +129,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190329.0 +home-assistant-frontend==20190331.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 804f1d1cc8fc031e30383135b1dcd1cbfb9dc638 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 20:01:23 -0700 Subject: [PATCH 305/605] Update translations --- .../components/axis/.translations/no.json | 26 ++++++++++++++ .../components/axis/.translations/pt.json | 12 +++++++ .../components/deconz/.translations/pt.json | 4 +-- .../components/esphome/.translations/no.json | 2 +- .../components/esphome/.translations/pt.json | 2 +- .../components/heos/.translations/ca.json | 20 +++++++++++ .../components/heos/.translations/en.json | 34 +++++++++---------- .../components/heos/.translations/no.json | 5 +++ .../components/heos/.translations/ru.json | 20 +++++++++++ .../homematicip_cloud/.translations/pt.json | 2 +- .../components/ps4/.translations/da.json | 3 ++ .../components/ps4/.translations/no.json | 9 +++++ .../components/ps4/.translations/pl.json | 9 +++++ .../components/ps4/.translations/ru.json | 4 +-- .../components/ps4/.translations/zh-Hant.json | 9 +++++ 15 files changed, 137 insertions(+), 24 deletions(-) create mode 100644 homeassistant/components/axis/.translations/no.json create mode 100644 homeassistant/components/axis/.translations/pt.json create mode 100644 homeassistant/components/heos/.translations/ca.json create mode 100644 homeassistant/components/heos/.translations/no.json create mode 100644 homeassistant/components/heos/.translations/ru.json diff --git a/homeassistant/components/axis/.translations/no.json b/homeassistant/components/axis/.translations/no.json new file mode 100644 index 00000000000000..94b5a1680b7156 --- /dev/null +++ b/homeassistant/components/axis/.translations/no.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "bad_config_file": "D\u00e5rlig data fra konfigurasjonsfilen", + "link_local_address": "Linking av lokale adresser st\u00f8ttes ikke" + }, + "error": { + "already_configured": "Enheten er allerede konfigurert", + "device_unavailable": "Enheten er ikke tilgjengelig", + "faulty_credentials": "Ugyldig brukerlegitimasjon" + }, + "step": { + "user": { + "data": { + "host": "Vert", + "password": "Passord", + "port": "Port", + "username": "Brukernavn" + }, + "title": "Sett opp Axis enhet" + } + }, + "title": "Axis enhet" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/pt.json b/homeassistant/components/axis/.translations/pt.json new file mode 100644 index 00000000000000..e71b890506da3a --- /dev/null +++ b/homeassistant/components/axis/.translations/pt.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "Palavra-passe", + "port": "Porta" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/.translations/pt.json b/homeassistant/components/deconz/.translations/pt.json index a0419b8baa42d1..47f5bb7db59a06 100644 --- a/homeassistant/components/deconz/.translations/pt.json +++ b/homeassistant/components/deconz/.translations/pt.json @@ -12,13 +12,13 @@ "init": { "data": { "host": "Servidor", - "port": "Porta (por omiss\u00e3o: '80')" + "port": "Porta" }, "title": "Defina o gateway deCONZ" }, "link": { "description": "Desbloqueie o seu gateway deCONZ para se registar no Home Assistant. \n\n 1. V\u00e1 para as configura\u00e7\u00f5es do sistema deCONZ \n 2. Pressione o bot\u00e3o \"Desbloquear Gateway\"", - "title": "Link com deCONZ" + "title": "Liga\u00e7\u00e3o com deCONZ" }, "options": { "data": { diff --git a/homeassistant/components/esphome/.translations/no.json b/homeassistant/components/esphome/.translations/no.json index 095e8825fbd11b..c71424b6f00e57 100644 --- a/homeassistant/components/esphome/.translations/no.json +++ b/homeassistant/components/esphome/.translations/no.json @@ -13,7 +13,7 @@ "data": { "password": "Passord" }, - "description": "Vennligst skriv inn passordet du har angitt i din konfigurasjon.", + "description": "Vennligst skriv inn passordet du har angitt i din konfigurasjon for {name}.", "title": "Skriv Inn Passord" }, "discovery_confirm": { diff --git a/homeassistant/components/esphome/.translations/pt.json b/homeassistant/components/esphome/.translations/pt.json index ea1e25c3024df4..7e4a85f3514866 100644 --- a/homeassistant/components/esphome/.translations/pt.json +++ b/homeassistant/components/esphome/.translations/pt.json @@ -13,7 +13,7 @@ "data": { "password": "Palavra-passe" }, - "description": "Por favor, insira a palavra-passe que colocou na configura\u00e7\u00e3o", + "description": "Por favor, insira a palavra-passe que colocou na configura\u00e7\u00e3o para {name}", "title": "Palavra-passe" }, "user": { diff --git a/homeassistant/components/heos/.translations/ca.json b/homeassistant/components/heos/.translations/ca.json new file mode 100644 index 00000000000000..1336d487953c6b --- /dev/null +++ b/homeassistant/components/heos/.translations/ca.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Nom\u00e9s pots configurar una \u00fanica connexi\u00f3 de Heos tot i que aquesta ja pot controlar tots els dispositius de la xarxa." + }, + "error": { + "connection_failure": "No es pot connectar amb l'amfitri\u00f3 especificat." + }, + "step": { + "user": { + "data": { + "access_token": "Amfitri\u00f3" + }, + "description": "Introdueix el nom d'amfitri\u00f3 o l'adre\u00e7a IP d'un dispositiu Heos (preferiblement un connectat a la xarxa per cable).", + "title": "Connexi\u00f3 amb Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/en.json b/homeassistant/components/heos/.translations/en.json index a272c0a2a0fd88..c38b69ea1c2f3e 100644 --- a/homeassistant/components/heos/.translations/en.json +++ b/homeassistant/components/heos/.translations/en.json @@ -1,20 +1,20 @@ { - "config": { - "title": "Heos", - "step": { - "user": { - "title": "Connect to Heos", - "description": "Please enter the host name or IP address of a Heos device (preferably one connected via wire to the network).", - "data": { - "access_token": "Host" - } - } - }, - "error": { - "connection_failure": "Unable to connect to the specified host." - }, - "abort": { - "already_setup": "You can only configure a single Heos connection as it will support all devices on the network." + "config": { + "abort": { + "already_setup": "You can only configure a single Heos connection as it will support all devices on the network." + }, + "error": { + "connection_failure": "Unable to connect to the specified host." + }, + "step": { + "user": { + "data": { + "access_token": "Host" + }, + "description": "Please enter the host name or IP address of a Heos device (preferably one connected via wire to the network).", + "title": "Connect to Heos" + } + }, + "title": "Heos" } - } } \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/no.json b/homeassistant/components/heos/.translations/no.json new file mode 100644 index 00000000000000..12ed8cc457a5d4 --- /dev/null +++ b/homeassistant/components/heos/.translations/no.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/ru.json b/homeassistant/components/heos/.translations/ru.json new file mode 100644 index 00000000000000..e78b9e4083b44b --- /dev/null +++ b/homeassistant/components/heos/.translations/ru.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "\u041d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Heos \u0432 \u0441\u0435\u0442\u0438." + }, + "error": { + "connection_failure": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0445\u043e\u0441\u0442\u0443" + }, + "step": { + "user": { + "data": { + "access_token": "\u0425\u043e\u0441\u0442" + }, + "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Heos (\u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0441\u0435\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u0431\u0435\u043b\u044c).", + "title": "Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/pt.json b/homeassistant/components/homematicip_cloud/.translations/pt.json index 8b431125ef06cc..0954f3ff4f9aab 100644 --- a/homeassistant/components/homematicip_cloud/.translations/pt.json +++ b/homeassistant/components/homematicip_cloud/.translations/pt.json @@ -21,7 +21,7 @@ "title": "Escolher ponto de acesso HomematicIP" }, "link": { - "description": "Pressione o bot\u00e3o azul no ponto de acesso e o bot\u00e3o enviar para registrar HomematicIP com o Home Assistant.\n\n![Localiza\u00e7\u00e3o do bot\u00e3o na ponte](/ static/images/config_flows/config_homematicip_cloud.png)", + "description": "Pressione o bot\u00e3o azul no ponto de acesso e o bot\u00e3o enviar para registrar HomematicIP com o Home Assistant.\n\n![Localiza\u00e7\u00e3o do bot\u00e3o na bridge](/ static/images/config_flows/config_homematicip_cloud.png)", "title": "Associar ponto de acesso" } }, diff --git a/homeassistant/components/ps4/.translations/da.json b/homeassistant/components/ps4/.translations/da.json index 7c5f9e7621cd70..801317a9e7f0c4 100644 --- a/homeassistant/components/ps4/.translations/da.json +++ b/homeassistant/components/ps4/.translations/da.json @@ -25,6 +25,9 @@ }, "description": "Indtast dine PlayStation 4 oplysninger. For 'PIN' skal du navigere til 'Indstillinger' p\u00e5 din PlayStation 4 konsol. G\u00e5 derefter til 'Indstillinger for mobilapp-forbindelse' og v\u00e6lg 'Tilf\u00f8j enhed'. Indtast den PIN der vises.", "title": "PlayStation 4" + }, + "mode": { + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/no.json b/homeassistant/components/ps4/.translations/no.json index 32687882da2e4d..8907032d83e30e 100644 --- a/homeassistant/components/ps4/.translations/no.json +++ b/homeassistant/components/ps4/.translations/no.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "Klarte ikke \u00e5 koble til PlayStation 4. Bekreft at PIN koden er riktig.", + "no_ipaddress": "Angi IP adressen til din PlayStation 4 som du \u00f8nsker konfigurere.", "not_ready": "PlayStation 4 er ikke p\u00e5sl\u00e5tt eller koblet til nettverk." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Skriv inn PlayStation 4 informasjonen din. For 'PIN', naviger til 'Innstillinger' p\u00e5 PlayStation 4 konsollen, deretter navigerer du til 'Innstillinger for mobilapp forbindelse' og velger 'Legg til enhet'. Skriv inn PIN-koden som vises.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP- adresse (Ikke fyll ut hvis du bruker Auto Discovery).", + "mode": "Konfigureringsmodus" + }, + "description": "Velg modus for konfigurasjon. Feltet IP-adresse kan st\u00e5 tomt dersom du velger Auto Discovery, da enheter vil bli oppdaget automatisk.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/pl.json b/homeassistant/components/ps4/.translations/pl.json index eea4eda0810b56..d38dabe318842b 100644 --- a/homeassistant/components/ps4/.translations/pl.json +++ b/homeassistant/components/ps4/.translations/pl.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "Nie uda\u0142o si\u0119 sparowa\u0107 z PlayStation 4. Sprawd\u017a, czy PIN jest poprawny.", + "no_ipaddress": "Wprowad\u017a adres IP PlayStation 4, kt\u00f3ry chcesz skonfigurowa\u0107.", "not_ready": "PlayStation 4 nie jest w\u0142\u0105czona lub po\u0142\u0105czona z sieci\u0105." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Wprowad\u017a informacje o PlayStation 4. Aby uzyska\u0107 'PIN', przejd\u017a do 'Ustawienia' na konsoli PlayStation 4. Nast\u0119pnie przejd\u017a do 'Ustawienia po\u0142\u0105czenia aplikacji mobilnej' i wybierz 'Dodaj urz\u0105dzenie'. Wprowad\u017a wy\u015bwietlony kod PIN.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "Adres IP (pozostaw puste, je\u015bli u\u017cywasz funkcji Auto Discovery).", + "mode": "Tryb konfiguracji" + }, + "description": "Wybierz tryb konfiguracji. Pole adresu IP mo\u017cna pozostawi\u0107 puste, je\u015bli wybierzesz opcj\u0119 Auto Discovery, poniewa\u017c urz\u0105dzenia zostan\u0105 automatycznie wykryte.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/ru.json b/homeassistant/components/ps4/.translations/ru.json index 424d0964729975..a784a607ac37b8 100644 --- a/homeassistant/components/ps4/.translations/ru.json +++ b/homeassistant/components/ps4/.translations/ru.json @@ -8,13 +8,13 @@ "port_997_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 997. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/)." }, "error": { - "login_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 \u0441 PlayStation 4. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e PIN-\u043a\u043e\u0434 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439.", + "login_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 \u0441 PlayStation 4. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e PIN-\u043a\u043e\u0434 \u0432\u0432\u0435\u0434\u0435\u043d \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e.", "no_ipaddress": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 PlayStation 4.", "not_ready": "PlayStation 4 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0438\u043b\u0438 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043a \u0441\u0435\u0442\u0438." }, "step": { "creds": { - "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c**, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 'PS4 Second Screen' \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e 'Home-Assistant', \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c.", + "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c**, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 'PS4 Second Screen' \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e 'Home-Assistant'.", "title": "PlayStation 4" }, "link": { diff --git a/homeassistant/components/ps4/.translations/zh-Hant.json b/homeassistant/components/ps4/.translations/zh-Hant.json index b4f45986c1e96e..54740e2c727957 100644 --- a/homeassistant/components/ps4/.translations/zh-Hant.json +++ b/homeassistant/components/ps4/.translations/zh-Hant.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "PlayStation 4 \u914d\u5c0d\u5931\u6557\uff0c\u8acb\u78ba\u8a8d PIN \u78bc\u3002", + "no_ipaddress": "\u8f38\u5165\u6240\u8981\u8a2d\u5b9a\u7684 PlayStation 4 \u4e4b IP \u4f4d\u5740\u3002", "not_ready": "PlayStation 4 \u4e26\u672a\u958b\u555f\u6216\u672a\u9023\u7dda\u81f3\u7db2\u8def\u3002" }, "step": { @@ -25,6 +26,14 @@ }, "description": "\u8f38\u5165\u60a8\u7684 PlayStation 4 \u8cc7\u8a0a\uff0c\u300cPIN\u300d\u65bc PlayStation 4 \u4e3b\u6a5f\u7684\u300c\u8a2d\u5b9a\u300d\u5167\uff0c\u4e26\u65bc\u300c\u884c\u52d5\u7a0b\u5f0f\u9023\u7dda\u8a2d\u5b9a\uff08Mobile App Connection Settings\uff09\u300d\u4e2d\u9078\u64c7\u300c\u65b0\u589e\u88dd\u7f6e\u300d\u3002\u8f38\u5165\u6240\u986f\u793a\u7684 PIN \u78bc\u3002", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "IP \u4f4d\u5740\uff08\u5982\u679c\u4f7f\u7528\u81ea\u52d5\u63a2\u7d22\u65b9\u5f0f\uff0c\u8acb\u4fdd\u7559\u7a7a\u767d\uff09\u3002", + "mode": "\u8a2d\u5b9a\u6a21\u5f0f" + }, + "description": "\u9078\u64c7\u6a21\u5f0f\u4ee5\u9032\u884c\u8a2d\u5b9a\u3002\u5047\u5982\u9078\u64c7\u81ea\u52d5\u63a2\u7d22\u6a21\u5f0f\u7684\u8a71\uff0c\u7531\u65bc\u6703\u81ea\u52d5\u9032\u884c\u88dd\u7f6e\u641c\u5c0b\uff0cIP \u4f4d\u5740\u53ef\u4fdd\u7559\u70ba\u7a7a\u767d\u3002", + "title": "PlayStation 4" } }, "title": "PlayStation 4" From ec9a58442b6a5324a36a9ad5e1245567e7505112 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 19:52:44 -0700 Subject: [PATCH 306/605] Updated frontend to 20190331.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 3baea2008b144b..f0358dbd6cc8d6 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190329.0'] +REQUIREMENTS = ['home-assistant-frontend==20190331.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index c0b458119e2c4c..09a447b2a20662 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -548,7 +548,7 @@ hole==0.3.0 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190329.0 +home-assistant-frontend==20190331.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 003c9fa43cbb65..27d96c5e606b2d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -129,7 +129,7 @@ hdate==0.8.7 holidays==0.9.10 # homeassistant.components.frontend -home-assistant-frontend==20190329.0 +home-assistant-frontend==20190331.0 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 8af70d5d19f7bc980a41d1e06425d7de2f29969a Mon Sep 17 00:00:00 2001 From: giefca Date: Sat, 30 Mar 2019 04:51:47 +0100 Subject: [PATCH 307/605] Google assistant: add blinds trait for covers (#22336) * Update const.py * Update smart_home.py * Update trait.py * Update test_trait.py * Update smart_home.py * Update test_trait.py * Update trait.py * Update trait.py * Update test_trait.py * Update test_trait.py * Update __init__.py * Update test_trait.py * Change email * Trying to correct CLA * Update __init__.py * Update trait.py * Update trait.py * Update trait.py * Update trait.py * Update __init__.py * Update test_trait.py * Update test_google_assistant.py * Update trait.py * Update trait.py * Update test_trait.py * Update test_trait.py --- .../components/google_assistant/const.py | 1 + .../components/google_assistant/smart_home.py | 4 +- .../components/google_assistant/trait.py | 92 +++++++++++++----- tests/components/google_assistant/__init__.py | 20 ++-- .../components/google_assistant/test_trait.py | 94 ++++++------------- 5 files changed, 110 insertions(+), 101 deletions(-) diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index 543404dd34e358..852ea2469a2ff2 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -29,6 +29,7 @@ TYPE_FAN = PREFIX_TYPES + 'FAN' TYPE_THERMOSTAT = PREFIX_TYPES + 'THERMOSTAT' TYPE_LOCK = PREFIX_TYPES + 'LOCK' +TYPE_BLINDS = PREFIX_TYPES + 'BLINDS' SERVICE_REQUEST_SYNC = 'request_sync' HOMEGRAPH_URL = 'https://homegraph.googleapis.com/' diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index 88cbea345b1b82..d84c8037c60bef 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -31,7 +31,7 @@ from . import trait from .const import ( TYPE_LIGHT, TYPE_LOCK, TYPE_SCENE, TYPE_SWITCH, TYPE_VACUUM, - TYPE_THERMOSTAT, TYPE_FAN, TYPE_CAMERA, + TYPE_THERMOSTAT, TYPE_FAN, TYPE_CAMERA, TYPE_BLINDS, CONF_ALIASES, CONF_ROOM_HINT, ERR_FUNCTION_NOT_SUPPORTED, ERR_PROTOCOL_ERROR, ERR_DEVICE_OFFLINE, ERR_UNKNOWN_ERROR, @@ -45,7 +45,7 @@ DOMAIN_TO_GOOGLE_TYPES = { camera.DOMAIN: TYPE_CAMERA, climate.DOMAIN: TYPE_THERMOSTAT, - cover.DOMAIN: TYPE_SWITCH, + cover.DOMAIN: TYPE_BLINDS, fan.DOMAIN: TYPE_FAN, group.DOMAIN: TYPE_SWITCH, input_boolean.DOMAIN: TYPE_SWITCH, diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index bd903575762ff3..81918ff2e886ad 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -48,6 +48,7 @@ TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock' TRAIT_FANSPEED = PREFIX_TRAITS + 'FanSpeed' TRAIT_MODES = PREFIX_TRAITS + 'Modes' +TRAIT_OPENCLOSE = PREFIX_TRAITS + 'OpenClose' PREFIX_COMMANDS = 'action.devices.commands.' COMMAND_ONOFF = PREFIX_COMMANDS + 'OnOff' @@ -66,6 +67,7 @@ COMMAND_LOCKUNLOCK = PREFIX_COMMANDS + 'LockUnlock' COMMAND_FANSPEED = PREFIX_COMMANDS + 'SetFanSpeed' COMMAND_MODES = PREFIX_COMMANDS + 'SetModes' +COMMAND_OPENCLOSE = PREFIX_COMMANDS + 'OpenClose' TRAITS = [] @@ -128,8 +130,6 @@ def supported(domain, features): """Test if state is supported.""" if domain == light.DOMAIN: return features & light.SUPPORT_BRIGHTNESS - if domain == cover.DOMAIN: - return features & cover.SUPPORT_SET_POSITION if domain == media_player.DOMAIN: return features & media_player.SUPPORT_VOLUME_SET @@ -149,11 +149,6 @@ def query_attributes(self): if brightness is not None: response['brightness'] = int(100 * (brightness / 255)) - elif domain == cover.DOMAIN: - position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) - if position is not None: - response['brightness'] = position - elif domain == media_player.DOMAIN: level = self.state.attributes.get( media_player.ATTR_MEDIA_VOLUME_LEVEL) @@ -173,12 +168,6 @@ async def execute(self, command, data, params): ATTR_ENTITY_ID: self.state.entity_id, light.ATTR_BRIGHTNESS_PCT: params['brightness'] }, blocking=True, context=data.context) - elif domain == cover.DOMAIN: - await self.hass.services.async_call( - cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, { - ATTR_ENTITY_ID: self.state.entity_id, - cover.ATTR_POSITION: params['brightness'] - }, blocking=True, context=data.context) elif domain == media_player.DOMAIN: await self.hass.services.async_call( media_player.DOMAIN, media_player.SERVICE_VOLUME_SET, { @@ -254,7 +243,6 @@ def supported(domain, features): switch.DOMAIN, fan.DOMAIN, light.DOMAIN, - cover.DOMAIN, media_player.DOMAIN, ) @@ -264,22 +252,13 @@ def sync_attributes(self): def query_attributes(self): """Return OnOff query attributes.""" - if self.state.domain == cover.DOMAIN: - return {'on': self.state.state != cover.STATE_CLOSED} return {'on': self.state.state != STATE_OFF} async def execute(self, command, data, params): """Execute an OnOff command.""" domain = self.state.domain - if domain == cover.DOMAIN: - service_domain = domain - if params['on']: - service = cover.SERVICE_OPEN_COVER - else: - service = cover.SERVICE_CLOSE_COVER - - elif domain == group.DOMAIN: + if domain == group.DOMAIN: service_domain = HA_DOMAIN service = SERVICE_TURN_ON if params['on'] else SERVICE_TURN_OFF @@ -1047,3 +1026,68 @@ async def execute(self, command, data, params): ATTR_ENTITY_ID: self.state.entity_id, media_player.ATTR_INPUT_SOURCE: source }, blocking=True, context=data.context) + + +@register_trait +class OpenCloseTrait(_Trait): + """Trait to open and close a cover. + + https://developers.google.com/actions/smarthome/traits/openclose + """ + + name = TRAIT_OPENCLOSE + commands = [ + COMMAND_OPENCLOSE + ] + + @staticmethod + def supported(domain, features): + """Test if state is supported.""" + return domain == cover.DOMAIN + + def sync_attributes(self): + """Return opening direction.""" + return {} + + def query_attributes(self): + """Return state query attributes.""" + domain = self.state.domain + response = {} + + if domain == cover.DOMAIN: + position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) + if position is not None: + response['openPercent'] = position + else: + if self.state.state != cover.STATE_CLOSED: + response['openPercent'] = 100 + else: + response['openPercent'] = 0 + + return response + + async def execute(self, command, data, params): + """Execute an Open, close, Set position command.""" + domain = self.state.domain + + if domain == cover.DOMAIN: + position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) + if position is not None: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, { + ATTR_ENTITY_ID: self.state.entity_id, + cover.ATTR_POSITION: params['openPercent'] + }, blocking=True, context=data.context) + else: + if self.state.state != cover.STATE_CLOSED: + if params['openPercent'] < 100: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_CLOSE_COVER, { + ATTR_ENTITY_ID: self.state.entity_id + }, blocking=True, context=data.context) + else: + if params['openPercent'] > 0: + await self.hass.services.async_call( + cover.DOMAIN, cover.SERVICE_OPEN_COVER, { + ATTR_ENTITY_ID: self.state.entity_id + }, blocking=True, context=data.context) diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index a8ea4a3f8881d0..331c6d2d9f5082 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -1,3 +1,5 @@ + + """Tests for the Google Assistant integration.""" DEMO_DEVICES = [{ @@ -93,9 +95,9 @@ 'name': 'Living Room Window' }, 'traits': - ['action.devices.traits.OnOff', 'action.devices.traits.Brightness'], + ['action.devices.traits.OpenClose'], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.BLINDS', 'willReportState': False }, { @@ -105,9 +107,9 @@ 'name': 'Hall Window' }, 'traits': - ['action.devices.traits.OnOff', 'action.devices.traits.Brightness'], + ['action.devices.traits.OpenClose'], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.BLINDS', 'willReportState': False }, { @@ -115,16 +117,18 @@ 'name': { 'name': 'Garage Door' }, - 'traits': ['action.devices.traits.OnOff'], - 'type': 'action.devices.types.SWITCH', + 'traits': ['action.devices.traits.OpenClose'], + 'type': + 'action.devices.types.BLINDS', 'willReportState': False }, { 'id': 'cover.kitchen_window', 'name': { 'name': 'Kitchen Window' }, - 'traits': ['action.devices.traits.OnOff'], - 'type': 'action.devices.types.SWITCH', + 'traits': ['action.devices.traits.OpenClose'], + 'type': + 'action.devices.types.BLINDS', 'willReportState': False }, { 'id': 'group.all_covers', diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index e42e4bdc91505d..a0a710d3d8ce27 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -83,33 +83,6 @@ async def test_brightness_light(hass): } -async def test_brightness_cover(hass): - """Test brightness trait support for cover domain.""" - assert trait.BrightnessTrait.supported(cover.DOMAIN, - cover.SUPPORT_SET_POSITION) - - trt = trait.BrightnessTrait(hass, State('cover.bla', cover.STATE_OPEN, { - cover.ATTR_CURRENT_POSITION: 75 - }), BASIC_CONFIG) - - assert trt.sync_attributes() == {} - - assert trt.query_attributes() == { - 'brightness': 75 - } - - calls = async_mock_service( - hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION) - await trt.execute( - trait.COMMAND_BRIGHTNESS_ABSOLUTE, BASIC_DATA, - {'brightness': 50}) - assert len(calls) == 1 - assert calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - cover.ATTR_POSITION: 50 - } - - async def test_brightness_media_player(hass): """Test brightness trait support for media player domain.""" assert trait.BrightnessTrait.supported(media_player.DOMAIN, @@ -358,46 +331,6 @@ async def test_onoff_light(hass): } -async def test_onoff_cover(hass): - """Test OnOff trait support for cover domain.""" - assert trait.OnOffTrait.supported(cover.DOMAIN, 0) - - trt_on = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_OPEN), - BASIC_CONFIG) - - assert trt_on.sync_attributes() == {} - - assert trt_on.query_attributes() == { - 'on': True - } - - trt_off = trait.OnOffTrait(hass, State('cover.bla', cover.STATE_CLOSED), - BASIC_CONFIG) - - assert trt_off.query_attributes() == { - 'on': False - } - - on_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': True}) - assert len(on_calls) == 1 - assert on_calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - } - - off_calls = async_mock_service(hass, cover.DOMAIN, - cover.SERVICE_CLOSE_COVER) - await trt_on.execute( - trait.COMMAND_ONOFF, BASIC_DATA, - {'on': False}) - assert len(off_calls) == 1 - assert off_calls[0].data == { - ATTR_ENTITY_ID: 'cover.bla', - } - - async def test_onoff_media_player(hass): """Test OnOff trait support for media_player domain.""" assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) @@ -1119,3 +1052,30 @@ async def test_modes(hass): 'entity_id': 'media_player.living_room', 'source': 'media' } + + +async def test_openclose_cover(hass): + """Test cover trait.""" + assert trait.OpenCloseTrait.supported(cover.DOMAIN, + cover.SUPPORT_SET_POSITION) + + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + cover.ATTR_CURRENT_POSITION: 75 + }), BASIC_CONFIG) + + assert trt.sync_attributes() == {} + + assert trt.query_attributes() == { + 'openPercent': 75 + } + + calls = async_mock_service( + hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION) + await trt.execute( + trait.COMMAND_OPENCLOSE, BASIC_DATA, + {'openPercent': 50}) + assert len(calls) == 1 + assert calls[0].data == { + ATTR_ENTITY_ID: 'cover.bla', + cover.ATTR_POSITION: 50 + } From a71fcfb6e5631d9231e99e99a59d093c723beecd Mon Sep 17 00:00:00 2001 From: drjared88 Date: Fri, 29 Mar 2019 21:53:01 -0600 Subject: [PATCH 308/605] Update Amcrest component to SUPPORT_STREAM (#22553) * Update camera.py Update Amcrest component to SUPPORT_STREAM to allow streaming in the UI and Google Assistant. * Update camera.py --- homeassistant/components/amcrest/camera.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 35d5e18fdd3505..63c2c720781a2b 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -2,7 +2,8 @@ import asyncio import logging -from homeassistant.components.camera import Camera +from homeassistant.components.camera import ( + Camera, SUPPORT_STREAM) from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import ( @@ -98,6 +99,11 @@ def name(self): """Return the name of this camera.""" return self._name + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + @property def stream_source(self): """Return the source of the stream.""" From de1605936592bde3733f59f9c56e47dbcdcec8d2 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sat, 30 Mar 2019 08:30:21 -0700 Subject: [PATCH 309/605] Fix name conflict in tests (#22556) * Fix name conflict in tests * Lint * Lint --- .../components/config/test_config_entries.py | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 852a5adf6a2cc3..6d2304433ab85f 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -31,10 +31,30 @@ def client(hass, hass_client): yield hass.loop.run_until_complete(hass_client()) +@HANDLERS.register('comp1') +class Comp1ConfigFlow: + """Config flow with options flow.""" + + @staticmethod + @callback + def async_get_options_flow(config, options): + """Get options flow.""" + pass + + +@HANDLERS.register('comp2') +class Comp2ConfigFlow: + """Config flow without options flow.""" + + def __init__(self): + """Init.""" + pass + + async def test_get_entries(hass, client): """Test get entries.""" MockConfigEntry( - domain='comp', + domain='comp1', title='Test 1', source='bla', connection_class=core_ce.CONN_CLASS_LOCAL_POLL, @@ -47,18 +67,6 @@ async def test_get_entries(hass, client): connection_class=core_ce.CONN_CLASS_ASSUMED, ).add_to_hass(hass) - class CompConfigFlow: - @staticmethod - @callback - def async_get_options_flow(config, options): - pass - HANDLERS['comp'] = CompConfigFlow() - - class Comp2ConfigFlow: - def __init__(self): - pass - HANDLERS['comp2'] = Comp2ConfigFlow() - resp = await client.get('/api/config/config_entries/entry') assert resp.status == 200 data = await resp.json() @@ -66,7 +74,7 @@ def __init__(self): entry.pop('entry_id') assert data == [ { - 'domain': 'comp', + 'domain': 'comp1', 'title': 'Test 1', 'source': 'bla', 'state': 'not_loaded', From 2e61ead4fd278cdf2f3ca93aa40726cec76404f1 Mon Sep 17 00:00:00 2001 From: drjared88 Date: Sun, 31 Mar 2019 16:12:55 -0600 Subject: [PATCH 310/605] Update ONVIF component to SUPPORT_STREAM (#22569) * Update Onvif component to SUPPORT_STREAM * Update camera.py * Update camera.py * Update camera.py Remove extra spaces. * lookup URL when camera is added to hass and add extra guards --- homeassistant/components/onvif/camera.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 36f1b18eebf0e7..09d47c3c0c9761 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -13,7 +13,8 @@ from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, ATTR_ENTITY_ID) -from homeassistant.components.camera import Camera, PLATFORM_SCHEMA +from homeassistant.components.camera import ( + Camera, PLATFORM_SCHEMA, SUPPORT_STREAM) from homeassistant.components.camera.const import DOMAIN from homeassistant.components.ffmpeg import ( DATA_FFMPEG, CONF_EXTRA_ARGUMENTS) @@ -187,13 +188,14 @@ async def async_added_to_hass(self): self.hass.data[ONVIF_DATA] = {} self.hass.data[ONVIF_DATA][ENTITIES] = [] self.hass.data[ONVIF_DATA][ENTITIES].append(self) + await self.hass.async_add_executor_job(self.obtain_input_uri) async def async_camera_image(self): """Return a still image response from the camera.""" from haffmpeg.tools import ImageFrame, IMAGE_JPEG if not self._input: - await self.hass.async_add_job(self.obtain_input_uri) + await self.hass.async_add_executor_job(self.obtain_input_uri) if not self._input: return None @@ -210,7 +212,7 @@ async def handle_async_mjpeg_stream(self, request): from haffmpeg.camera import CameraMjpeg if not self._input: - await self.hass.async_add_job(self.obtain_input_uri) + await self.hass.async_add_executor_job(self.obtain_input_uri) if not self._input: return None @@ -228,6 +230,18 @@ async def handle_async_mjpeg_stream(self, request): finally: await stream.close() + @property + def supported_features(self): + """Return supported features.""" + if self._input: + return SUPPORT_STREAM + return 0 + + @property + def stream_source(self): + """Return the stream source.""" + return self._input + @property def name(self): """Return the name of this camera.""" From 0e42cb64d6dde8bda63274b3eaa4aab9fe977b32 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 17:14:19 -0700 Subject: [PATCH 311/605] Add stream to the default config (#22602) --- homeassistant/components/default_config/__init__.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/default_config/__init__.py b/homeassistant/components/default_config/__init__.py index 888a4d51c956f7..6743893888d819 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -1,7 +1,11 @@ """Component providing default configuration for new users.""" +try: + import av +except ImportError: + av = None DOMAIN = 'default_config' -DEPENDENCIES = ( +DEPENDENCIES = [ 'automation', 'cloud', 'config', @@ -17,7 +21,10 @@ 'system_health', 'updater', 'zeroconf', -) +] +# Only automatically set up the stream component when dependency installed +if av is not None: + DEPENDENCIES.append('stream') async def async_setup(hass, config): From 734a67ede003b252639e4dcaf5a4df149ac2a6e3 Mon Sep 17 00:00:00 2001 From: carstenschroeder Date: Mon, 1 Apr 2019 05:28:43 +0200 Subject: [PATCH 312/605] Refactor of ADS integration and introduce ADSEntity (#22583) * Prevent toogle to false at restart * change to asyncio.run_coroutine_threadsafe * refactor ADS platforms; introduce AdsEntity * fix hound findings * some formatting * remove redundant def. * fix useless super delegation * fix inconsistent ADS data type for brightness * fix requested changes * fix comment --- homeassistant/components/ads/__init__.py | 71 ++++++++++++++ homeassistant/components/ads/binary_sensor.py | 63 ++---------- homeassistant/components/ads/light.py | 97 ++++--------------- homeassistant/components/ads/sensor.py | 53 +++------- homeassistant/components/ads/switch.py | 70 ++----------- 5 files changed, 121 insertions(+), 233 deletions(-) diff --git a/homeassistant/components/ads/__init__.py b/homeassistant/components/ads/__init__.py index 1b90e645af4dc1..5ab53e3acd2f87 100644 --- a/homeassistant/components/ads/__init__.py +++ b/homeassistant/components/ads/__init__.py @@ -4,12 +4,15 @@ import logging import ctypes from collections import namedtuple +import asyncio +import async_timeout import voluptuous as vol from homeassistant.const import ( CONF_DEVICE, CONF_IP_ADDRESS, CONF_PORT, EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity REQUIREMENTS = ['pyads==3.0.7'] @@ -31,6 +34,9 @@ CONF_ADS_VAR = 'adsvar' CONF_ADS_VAR_BRIGHTNESS = 'adsvar_brightness' +STATE_KEY_STATE = 'state' +STATE_KEY_BRIGHTNESS = 'brightness' + DOMAIN = 'ads' SERVICE_WRITE_DATA_BY_NAME = 'write_data_by_name' @@ -210,3 +216,68 @@ def _device_notification_callback(self, notification, name): _LOGGER.warning("No callback available for this datatype") notification_item.callback(notification_item.name, value) + + +class AdsEntity(Entity): + """Representation of ADS entity.""" + + def __init__(self, ads_hub, name, ads_var): + """Initialize ADS binary sensor.""" + self._name = name + self._unique_id = ads_var + self._state_dict = {} + self._state_dict[STATE_KEY_STATE] = None + self._ads_hub = ads_hub + self._ads_var = ads_var + self._event = None + + async def async_initialize_device( + self, ads_var, plctype, state_key=STATE_KEY_STATE, factor=None): + """Register device notification.""" + def update(name, value): + """Handle device notifications.""" + _LOGGER.debug('Variable %s changed its value to %d', name, value) + + if factor is None: + self._state_dict[state_key] = value + else: + self._state_dict[state_key] = value / factor + + asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) + self.schedule_update_ha_state() + + async def async_event_set(): + """Set event in async context.""" + self._event.set() + + self._event = asyncio.Event() + + await self.hass.async_add_executor_job( + self._ads_hub.add_device_notification, + ads_var, plctype, update) + try: + with async_timeout.timeout(10): + await self._event.wait() + except asyncio.TimeoutError: + _LOGGER.debug('Variable %s: Timeout during first update', + ads_var) + + @property + def name(self): + """Return the default name of the binary sensor.""" + return self._name + + @property + def unique_id(self): + """Return an unique identifier for this entity.""" + return self._unique_id + + @property + def should_poll(self): + """Return False because entity pushes its state to HA.""" + return False + + @property + def available(self): + """Return False if state has not been updated yet.""" + return self._state_dict[STATE_KEY_STATE] is not None diff --git a/homeassistant/components/ads/binary_sensor.py b/homeassistant/components/ads/binary_sensor.py index 91cd60771d9318..baa44cb498fe04 100644 --- a/homeassistant/components/ads/binary_sensor.py +++ b/homeassistant/components/ads/binary_sensor.py @@ -1,7 +1,5 @@ """Support for ADS binary sensors.""" import logging -import asyncio -import async_timeout import voluptuous as vol @@ -10,7 +8,7 @@ from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME import homeassistant.helpers.config_validation as cv -from . import CONF_ADS_VAR, DATA_ADS +from . import CONF_ADS_VAR, DATA_ADS, AdsEntity, STATE_KEY_STATE _LOGGER = logging.getLogger(__name__) @@ -36,70 +34,25 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities([ads_sensor]) -class AdsBinarySensor(BinarySensorDevice): +class AdsBinarySensor(AdsEntity, BinarySensorDevice): """Representation of ADS binary sensors.""" def __init__(self, ads_hub, name, ads_var, device_class): """Initialize ADS binary sensor.""" - self._name = name - self._unique_id = ads_var - self._state = None + super().__init__(ads_hub, name, ads_var) self._device_class = device_class or 'moving' - self._ads_hub = ads_hub - self.ads_var = ads_var - self._event = None async def async_added_to_hass(self): """Register device notification.""" - def update(name, value): - """Handle device notifications.""" - _LOGGER.debug('Variable %s changed its value to %d', name, value) - self._state = value - asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) - self.schedule_update_ha_state() - - async def async_event_set(): - """Set event in async context.""" - self._event.set() - - self._event = asyncio.Event() - - await self.hass.async_add_executor_job( - self._ads_hub.add_device_notification, - self.ads_var, self._ads_hub.PLCTYPE_BOOL, update) - try: - with async_timeout.timeout(10): - await self._event.wait() - except asyncio.TimeoutError: - _LOGGER.debug('Variable %s: Timeout during first update', - self.ads_var) + await self.async_initialize_device(self._ads_var, + self._ads_hub.PLCTYPE_BOOL) @property - def name(self): - """Return the default name of the binary sensor.""" - return self._name - - @property - def unique_id(self): - """Return an unique identifier for this entity.""" - return self._unique_id + def is_on(self): + """Return True if the entity is on.""" + return self._state_dict[STATE_KEY_STATE] @property def device_class(self): """Return the device class.""" return self._device_class - - @property - def is_on(self): - """Return if the binary sensor is on.""" - return self._state - - @property - def should_poll(self): - """Return False because entity pushes its state to HA.""" - return False - - @property - def available(self): - """Return False if state has not been updated yet.""" - return self._state is not None diff --git a/homeassistant/components/ads/light.py b/homeassistant/components/ads/light.py index 2ece1402907108..49961565dced77 100644 --- a/homeassistant/components/ads/light.py +++ b/homeassistant/components/ads/light.py @@ -1,7 +1,5 @@ """Support for ADS light sources.""" import logging -import asyncio -import async_timeout import voluptuous as vol @@ -10,12 +8,12 @@ from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv -from . import CONF_ADS_VAR, CONF_ADS_VAR_BRIGHTNESS, DATA_ADS +from . import CONF_ADS_VAR, CONF_ADS_VAR_BRIGHTNESS, DATA_ADS, \ + AdsEntity, STATE_KEY_BRIGHTNESS, STATE_KEY_STATE _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ads'] DEFAULT_NAME = 'ADS Light' -CONF_ADSVAR_BRIGHTNESS = 'adsvar_brightness' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADS_VAR): cv.string, vol.Optional(CONF_ADS_VAR_BRIGHTNESS): cv.string, @@ -35,107 +33,54 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name)]) -class AdsLight(Light): +class AdsLight(AdsEntity, Light): """Representation of ADS light.""" def __init__(self, ads_hub, ads_var_enable, ads_var_brightness, name): """Initialize AdsLight entity.""" - self._ads_hub = ads_hub - self._on_state = None - self._brightness = None - self._name = name - self._unique_id = ads_var_enable - self.ads_var_enable = ads_var_enable - self.ads_var_brightness = ads_var_brightness - self._event = None + super().__init__(ads_hub, name, ads_var_enable) + self._state_dict[STATE_KEY_BRIGHTNESS] = None + self._ads_var_brightness = ads_var_brightness async def async_added_to_hass(self): """Register device notification.""" - def update_on_state(name, value): - """Handle device notifications for state.""" - _LOGGER.debug('Variable %s changed its value to %d', name, value) - self._on_state = value - asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) - self.schedule_update_ha_state() - - async def async_event_set(): - """Set event in async context.""" - self._event.set() - - def update_brightness(name, value): - """Handle device notification for brightness.""" - _LOGGER.debug('Variable %s changed its value to %d', name, value) - self._brightness = value - self.schedule_update_ha_state() - - self._event = asyncio.Event() - - await self.hass.async_add_executor_job( - self._ads_hub.add_device_notification, - self.ads_var_enable, self._ads_hub.PLCTYPE_BOOL, update_on_state - ) - if self.ads_var_brightness is not None: - await self.hass.async_add_executor_job( - self._ads_hub.add_device_notification, - self.ads_var_brightness, self._ads_hub.PLCTYPE_INT, - update_brightness - ) - try: - with async_timeout.timeout(10): - await self._event.wait() - except asyncio.TimeoutError: - _LOGGER.debug('Variable %s: Timeout during first update', - self.ads_var_enable) + await self.async_initialize_device(self._ads_var, + self._ads_hub.PLCTYPE_BOOL) - @property - def name(self): - """Return the name of the device if any.""" - return self._name - - @property - def unique_id(self): - """Return an unique identifier for this entity.""" - return self._unique_id + if self._ads_var_brightness is not None: + await self.async_initialize_device(self._ads_var_brightness, + self._ads_hub.PLCTYPE_UINT, + STATE_KEY_BRIGHTNESS) @property def brightness(self): """Return the brightness of the light (0..255).""" - return self._brightness - - @property - def is_on(self): - """Return if light is on.""" - return self._on_state - - @property - def should_poll(self): - """Return False because entity pushes its state to HA.""" - return False + return self._state_dict[STATE_KEY_BRIGHTNESS] @property def supported_features(self): """Flag supported features.""" support = 0 - if self.ads_var_brightness is not None: + if self._ads_var_brightness is not None: support = SUPPORT_BRIGHTNESS return support @property - def available(self): - """Return False if state has not been updated yet.""" - return self._on_state is not None + def is_on(self): + """Return True if the entity is on.""" + return self._state_dict[STATE_KEY_STATE] def turn_on(self, **kwargs): """Turn the light on or set a specific dimmer value.""" brightness = kwargs.get(ATTR_BRIGHTNESS) - self._ads_hub.write_by_name(self.ads_var_enable, True, + self._ads_hub.write_by_name(self._ads_var, True, self._ads_hub.PLCTYPE_BOOL) - if self.ads_var_brightness is not None and brightness is not None: - self._ads_hub.write_by_name(self.ads_var_brightness, brightness, + if self._ads_var_brightness is not None and brightness is not None: + self._ads_hub.write_by_name(self._ads_var_brightness, brightness, self._ads_hub.PLCTYPE_UINT) def turn_off(self, **kwargs): """Turn the light off.""" - self._ads_hub.write_by_name(self.ads_var_enable, False, + self._ads_hub.write_by_name(self._ads_var, False, self._ads_hub.PLCTYPE_BOOL) diff --git a/homeassistant/components/ads/sensor.py b/homeassistant/components/ads/sensor.py index 118a669a7ad4a1..e74b8753d4b042 100644 --- a/homeassistant/components/ads/sensor.py +++ b/homeassistant/components/ads/sensor.py @@ -7,9 +7,9 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity -from . import CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR +from . import CONF_ADS_FACTOR, CONF_ADS_TYPE, CONF_ADS_VAR, \ + AdsEntity, STATE_KEY_STATE _LOGGER = logging.getLogger(__name__) @@ -43,60 +43,31 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities([entity]) -class AdsSensor(Entity): +class AdsSensor(AdsEntity): """Representation of an ADS sensor entity.""" def __init__(self, ads_hub, ads_var, ads_type, name, unit_of_measurement, factor): """Initialize AdsSensor entity.""" - self._ads_hub = ads_hub - self._name = name - self._unique_id = ads_var - self._value = None + super().__init__(ads_hub, name, ads_var) self._unit_of_measurement = unit_of_measurement - self.ads_var = ads_var - self.ads_type = ads_type - self.factor = factor + self._ads_type = ads_type + self._factor = factor async def async_added_to_hass(self): """Register device notification.""" - def update(name, value): - """Handle device notifications.""" - _LOGGER.debug("Variable %s changed its value to %d", name, value) - - # If factor is set use it otherwise not - if self.factor is None: - self._value = value - else: - self._value = value / self.factor - self.schedule_update_ha_state() - - self.hass.async_add_job( - self._ads_hub.add_device_notification, - self.ads_var, self._ads_hub.ADS_TYPEMAP[self.ads_type], update - ) - - @property - def name(self): - """Return the name of the entity.""" - return self._name - - @property - def unique_id(self): - """Return an unique identifier for this entity.""" - return self._unique_id + await self.async_initialize_device( + self._ads_var, + self._ads_hub.ADS_TYPEMAP[self._ads_type], + STATE_KEY_STATE, + self._factor) @property def state(self): """Return the state of the device.""" - return self._value + return self._state_dict[STATE_KEY_STATE] @property def unit_of_measurement(self): """Return the unit of measurement.""" return self._unit_of_measurement - - @property - def should_poll(self): - """Return False because entity pushes its state.""" - return False diff --git a/homeassistant/components/ads/switch.py b/homeassistant/components/ads/switch.py index 3d2189d2ede93a..0dfbeb811a068b 100644 --- a/homeassistant/components/ads/switch.py +++ b/homeassistant/components/ads/switch.py @@ -1,16 +1,13 @@ """Support for ADS switch platform.""" import logging -import asyncio -import async_timeout import voluptuous as vol -from homeassistant.components.switch import PLATFORM_SCHEMA +from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import ToggleEntity -from . import CONF_ADS_VAR, DATA_ADS +from . import CONF_ADS_VAR, DATA_ADS, AdsEntity, STATE_KEY_STATE _LOGGER = logging.getLogger(__name__) @@ -34,74 +31,25 @@ def setup_platform(hass, config, add_entities, discovery_info=None): add_entities([AdsSwitch(ads_hub, name, ads_var)]) -class AdsSwitch(ToggleEntity): +class AdsSwitch(AdsEntity, SwitchDevice): """Representation of an ADS switch device.""" - def __init__(self, ads_hub, name, ads_var): - """Initialize the AdsSwitch entity.""" - self._ads_hub = ads_hub - self._on_state = None - self._name = name - self._unique_id = ads_var - self.ads_var = ads_var - self._event = None - async def async_added_to_hass(self): """Register device notification.""" - def update(name, value): - """Handle device notification.""" - _LOGGER.debug("Variable %s changed its value to %d", name, value) - self._on_state = value - asyncio.run_coroutine_threadsafe(async_event_set(), self.hass.loop) - self.schedule_update_ha_state() - - async def async_event_set(): - """Set event in async context.""" - self._event.set() - - self._event = asyncio.Event() - - await self.hass.async_add_executor_job( - self._ads_hub.add_device_notification, - self.ads_var, self._ads_hub.PLCTYPE_BOOL, update) - try: - with async_timeout.timeout(10): - await self._event.wait() - except asyncio.TimeoutError: - _LOGGER.debug('Variable %s: Timeout during first update', - self.ads_var) + await self.async_initialize_device(self._ads_var, + self._ads_hub.PLCTYPE_BOOL) @property def is_on(self): - """Return if the switch is turned on.""" - return self._on_state - - @property - def name(self): - """Return the name of the entity.""" - return self._name - - @property - def unique_id(self): - """Return an unique identifier for this entity.""" - return self._unique_id - - @property - def should_poll(self): - """Return False because entity pushes its state to HA.""" - return False - - @property - def available(self): - """Return False if state has not been updated yet.""" - return self._on_state is not None + """Return True if the entity is on.""" + return self._state_dict[STATE_KEY_STATE] def turn_on(self, **kwargs): """Turn the switch on.""" self._ads_hub.write_by_name( - self.ads_var, True, self._ads_hub.PLCTYPE_BOOL) + self._ads_var, True, self._ads_hub.PLCTYPE_BOOL) def turn_off(self, **kwargs): """Turn the switch off.""" self._ads_hub.write_by_name( - self.ads_var, False, self._ads_hub.PLCTYPE_BOOL) + self._ads_var, False, self._ads_hub.PLCTYPE_BOOL) From fbb28c401ea459b55a94fac35cc5aa2aaae50b73 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 31 Mar 2019 20:30:30 -0700 Subject: [PATCH 313/605] Bumped version to 0.91.0b4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 48476c4fa909c2..ea4b51e4bd9274 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b3' +PATCH_VERSION = '0b4' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 7bd8c0d39a2ba847a8e3413896b42b21fc63c915 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 31 Mar 2019 21:30:45 -0700 Subject: [PATCH 314/605] Add new mobile_app webhook command: get_zones (#22604) ## Description: Adds a new `mobile_app` webhook command, `get_zones`, which just returns all zones. ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/mobile_app/const.py | 5 +-- .../components/mobile_app/helpers.py | 5 +-- .../components/mobile_app/webhook.py | 32 +++++++++++++------ homeassistant/components/zone/config_flow.py | 2 +- tests/components/mobile_app/test_webhook.py | 26 +++++++++++++++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 3aa4626da29c41..38897056c11289 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -65,6 +65,7 @@ WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_GET_ZONES = 'get_zones' WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location' @@ -72,8 +73,8 @@ WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES] diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index 60bd8b4e1d6bec..ee593588ef8ff6 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -6,6 +6,7 @@ from aiohttp.web import json_response, Response from homeassistant.core import Context +from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.typing import HomeAssistantType from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME, @@ -133,9 +134,9 @@ def savable_state(hass: HomeAssistantType) -> Dict: def webhook_response(data, *, registration: Dict, status: int = 200, headers: Dict = None) -> Response: """Return a encrypted response if registration supports it.""" - data = json.dumps(data) + data = json.dumps(data, cls=JSONEncoder) - if CONF_SECRET in registration: + if registration[ATTR_SUPPORTS_ENCRYPTION]: keylen, encrypt = setup_encrypt() key = registration[CONF_SECRET].encode("utf-8") diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index aafa6046d110a5..71c6d0d66734e0 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -9,6 +9,8 @@ DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) +from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN + from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, CONF_WEBHOOK_ID, HTTP_BAD_REQUEST, HTTP_CREATED) @@ -33,9 +35,10 @@ DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -87,16 +90,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA] webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_data) - if webhook_type not in WEBHOOK_SCHEMAS: + if webhook_type not in WEBHOOK_TYPES: _LOGGER.error('Received invalid webhook type: %s', webhook_type) return empty_okay_response() - try: - data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) - except vol.Invalid as ex: - err = vol.humanize.humanize_error(webhook_payload, ex) - _LOGGER.error('Received invalid webhook payload: %s', err) - return empty_okay_response(headers=headers) + data = webhook_payload + + if webhook_type in WEBHOOK_SCHEMAS: + try: + data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) + except vol.Invalid as ex: + err = vol.humanize.humanize_error(webhook_payload, ex) + _LOGGER.error('Received invalid webhook payload: %s', err) + return empty_okay_response(headers=headers) context = registration_context(registration) @@ -261,3 +267,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, return webhook_response(resp, registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_GET_ZONES: + zones = (hass.states.get(entity_id) for entity_id + in sorted(hass.states.async_entity_ids(ZONE_DOMAIN))) + return webhook_response(list(zones), registration=registration, + headers=headers) diff --git a/homeassistant/components/zone/config_flow.py b/homeassistant/components/zone/config_flow.py index bf221a828adc33..a7b968676d6d8b 100644 --- a/homeassistant/components/zone/config_flow.py +++ b/homeassistant/components/zone/config_flow.py @@ -14,7 +14,7 @@ @callback def configured_zones(hass): - """Return a set of the configured hosts.""" + """Return a set of the configured zones.""" return set((slugify(entry.data[CONF_NAME])) for entry in hass.config_entries.async_entries(DOMAIN)) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index a70e8ba1275290..ad19a70a806ac2 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -4,8 +4,10 @@ import pytest from homeassistant.components.mobile_app.const import CONF_SECRET +from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.core import callback +from homeassistant.setup import async_setup_component from tests.common import async_mock_service @@ -100,6 +102,30 @@ async def test_webhook_update_registration(webhook_client, hass_client): # noqa assert CONF_SECRET not in update_json +async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 + """Test that we can get zones properly.""" + await async_setup_component(hass, ZONE_DOMAIN, { + ZONE_DOMAIN: { + 'name': 'test', + 'latitude': 32.880837, + 'longitude': -117.237561, + 'radius': 250, + } + }) + + resp = await webhook_client.post( + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), + json={'type': 'get_zones'} + ) + + assert resp.status == 200 + + json = await resp.json() + assert len(json) == 1 + assert json[0]['entity_id'] == 'zone.home' + + async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 create_registrations, # noqa: F401, F811, E501 caplog): # noqa: E501 F811 From a61181b10cca2f6517dac74f0f13465f1811afec Mon Sep 17 00:00:00 2001 From: N1nja98 <47512532+N1nja98@users.noreply.github.com> Date: Mon, 1 Apr 2019 01:27:47 -0500 Subject: [PATCH 315/605] Fixed brightness reducing after each light change (#22606) self._brightness max is 255 and hsv brightness max is 100. Assigning 255 based brightness value directly with 100 based hsv reduces brightness eventually to zero. --- homeassistant/components/zengge/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zengge/light.py b/homeassistant/components/zengge/light.py index b283b8611dc640..69ca3da0af9d48 100644 --- a/homeassistant/components/zengge/light.py +++ b/homeassistant/components/zengge/light.py @@ -157,6 +157,6 @@ def update(self): rgb = self._bulb.get_colour() hsv = color_util.color_RGB_to_hsv(*rgb) self._hs_color = hsv[:2] - self._brightness = hsv[2] + self._brightness = (hsv[2] / 100) * 255 self._white = self._bulb.get_white() self._state = self._bulb.get_on() From 282fd225c9d621b773f289dc7acfc30ebd9639df Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Mon, 1 Apr 2019 08:47:29 +0200 Subject: [PATCH 316/605] Add netgear_lte connection sensors (#22558) --- homeassistant/components/netgear_lte/__init__.py | 2 +- homeassistant/components/netgear_lte/sensor.py | 11 +++++++++++ .../components/netgear_lte/sensor_types.py | 13 +++++++++++++ requirements_all.txt | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index a259a361be42a7..c611c65797de99 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -20,7 +20,7 @@ from . import sensor_types -REQUIREMENTS = ['eternalegypt==0.0.5'] +REQUIREMENTS = ['eternalegypt==0.0.6'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 42b0ddfa054806..8141444bfc4af0 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -36,6 +36,8 @@ async def async_setup_platform( sensors.append(SMSSensor(modem_data, sensor_type)) elif sensor_type == SENSOR_USAGE: sensors.append(UsageSensor(modem_data, sensor_type)) + else: + sensors.append(GenericSensor(modem_data, sensor_type)) async_add_entities(sensors) @@ -106,3 +108,12 @@ class UsageSensor(LTESensor): def state(self): """Return the state of the sensor.""" return round(self.modem_data.data.usage / 1024**2, 1) + + +class GenericSensor(LTESensor): + """Sensor entity with raw state.""" + + @property + def state(self): + """Return the state of the sensor.""" + return getattr(self.modem_data.data, self.sensor_type) diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py index 673f929d9adae6..5a56404abda18d 100644 --- a/homeassistant/components/netgear_lte/sensor_types.py +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -6,6 +6,19 @@ SENSOR_UNITS = { SENSOR_SMS: 'unread', SENSOR_USAGE: 'MiB', + 'radio_quality': '%', + 'rx_level': 'dBm', + 'tx_level': 'dBm', + 'upstream': None, + 'wire_connected': None, + 'mobile_connected': None, + 'connection_text': None, + 'connection_type': None, + 'current_ps_service_type': None, + 'register_network_display': None, + 'roaming': None, + 'current_band': None, + 'cell_id': None, } ALL = list(SENSOR_UNITS) diff --git a/requirements_all.txt b/requirements_all.txt index e3cc4a349541c8..ef42a58e97cf42 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -407,7 +407,7 @@ ephem==3.7.6.0 epson-projector==0.1.3 # homeassistant.components.netgear_lte -eternalegypt==0.0.5 +eternalegypt==0.0.6 # homeassistant.components.keyboard_remote # evdev==0.6.1 From c96804954cb4b6ecad77005b9194f236a16b79f6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 01:22:51 -0700 Subject: [PATCH 317/605] Only allow admins to enable remote connection (#22609) * Only allow admins to enable remote connection * Protect WS API * Lint --- homeassistant/components/cloud/__init__.py | 9 ++++---- homeassistant/components/cloud/http_api.py | 2 ++ tests/components/cloud/test_init.py | 26 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 76a768385f8508..fca5b292033dca 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -187,10 +187,11 @@ async def _service_handler(service): await cloud.remote.disconnect() await prefs.async_update(remote_enabled=False) - hass.services.async_register( - DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler) - hass.services.async_register( - DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler) + empty_schema = vol.Schema({}) + hass.helpers.service.async_register_admin_service( + DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler, empty_schema) + hass.helpers.service.async_register_admin_service( + DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler, empty_schema) await http_api.async_setup(hass) hass.async_create_task(hass.helpers.discovery.async_load_platform( diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 212bdfb4bf8ae5..d997d98d06e5e7 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -422,6 +422,7 @@ def _account_data(cloud): } +@websocket_api.require_admin @_require_cloud_login @websocket_api.async_response @_ws_handle_cloud_errors @@ -436,6 +437,7 @@ async def websocket_remote_connect(hass, connection, msg): connection.send_result(msg['id'], _account_data(cloud)) +@websocket_api.require_admin @_require_cloud_login @websocket_api.async_response @_ws_handle_cloud_errors diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index 0de395c8bbc97d..ea611c29df1c0b 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -1,6 +1,10 @@ """Test the cloud component.""" from unittest.mock import patch +import pytest + +from homeassistant.core import Context +from homeassistant.exceptions import Unauthorized from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components import cloud from homeassistant.components.cloud.const import DOMAIN @@ -34,7 +38,7 @@ async def test_constructor_loads_info_from_config(hass): assert cl.relayer == 'test-relayer' -async def test_remote_services(hass, mock_cloud_fixture): +async def test_remote_services(hass, mock_cloud_fixture, hass_read_only_user): """Setup cloud component and test services.""" cloud = hass.data[DOMAIN] @@ -58,6 +62,26 @@ async def test_remote_services(hass, mock_cloud_fixture): assert mock_disconnect.called assert not cloud.client.remote_autostart + # Test admin access required + non_admin_context = Context(user_id=hass_read_only_user.id) + + with patch( + "hass_nabucasa.remote.RemoteUI.connect", return_value=mock_coro() + ) as mock_connect, pytest.raises(Unauthorized): + await hass.services.async_call(DOMAIN, "remote_connect", blocking=True, + context=non_admin_context) + + assert mock_connect.called is False + + with patch( + "hass_nabucasa.remote.RemoteUI.disconnect", return_value=mock_coro() + ) as mock_disconnect, pytest.raises(Unauthorized): + await hass.services.async_call( + DOMAIN, "remote_disconnect", blocking=True, + context=non_admin_context) + + assert mock_disconnect.called is False + async def test_startup_shutdown_events(hass, mock_cloud_fixture): """Test if the cloud will start on startup event.""" From 42e3e878dfb43142c89046dc51ee939e32571621 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 05:07:12 -0700 Subject: [PATCH 318/605] Cloudhooks for webhook config flows (#22611) --- .../components/dialogflow/__init__.py | 5 ++ homeassistant/components/geofency/__init__.py | 5 ++ .../components/gpslogger/__init__.py | 5 ++ homeassistant/components/ifttt/__init__.py | 5 ++ homeassistant/components/locative/__init__.py | 6 ++- homeassistant/components/mailgun/__init__.py | 5 ++ homeassistant/components/twilio/__init__.py | 5 ++ homeassistant/helpers/config_entry_flow.py | 26 ++++++++-- tests/helpers/test_config_entry_flow.py | 52 ++++++++++++++++++- 9 files changed, 108 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/dialogflow/__init__.py b/homeassistant/components/dialogflow/__init__.py index 210aebe80d5cb6..1536fe3d2362ca 100644 --- a/homeassistant/components/dialogflow/__init__.py +++ b/homeassistant/components/dialogflow/__init__.py @@ -79,6 +79,11 @@ async def async_unload_entry(hass, entry): hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'Dialogflow Webhook', diff --git a/homeassistant/components/geofency/__init__.py b/homeassistant/components/geofency/__init__.py index f27798e9e0d745..88b72f02cc2e9f 100644 --- a/homeassistant/components/geofency/__init__.py +++ b/homeassistant/components/geofency/__init__.py @@ -133,6 +133,11 @@ async def async_unload_entry(hass, entry): await hass.config_entries.async_forward_entry_unload(entry, DEVICE_TRACKER) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'Geofency Webhook', diff --git a/homeassistant/components/gpslogger/__init__.py b/homeassistant/components/gpslogger/__init__.py index 12da63d8ebb3fd..6bc9d11a68e088 100644 --- a/homeassistant/components/gpslogger/__init__.py +++ b/homeassistant/components/gpslogger/__init__.py @@ -104,6 +104,11 @@ async def async_unload_entry(hass, entry): await hass.config_entries.async_forward_entry_unload(entry, DEVICE_TRACKER) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'GPSLogger Webhook', diff --git a/homeassistant/components/ifttt/__init__.py b/homeassistant/components/ifttt/__init__.py index 4ab361d41ebd6d..bad3984ea5bd20 100644 --- a/homeassistant/components/ifttt/__init__.py +++ b/homeassistant/components/ifttt/__init__.py @@ -108,6 +108,11 @@ async def async_unload_entry(hass, entry): hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'IFTTT Webhook', diff --git a/homeassistant/components/locative/__init__.py b/homeassistant/components/locative/__init__.py index e6a5b56ecda400..335ae4cfe1eae9 100644 --- a/homeassistant/components/locative/__init__.py +++ b/homeassistant/components/locative/__init__.py @@ -141,10 +141,14 @@ async def async_setup_entry(hass, entry): async def async_unload_entry(hass, entry): """Unload a config entry.""" hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) - await hass.config_entries.async_forward_entry_unload(entry, DEVICE_TRACKER) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'Locative Webhook', diff --git a/homeassistant/components/mailgun/__init__.py b/homeassistant/components/mailgun/__init__.py index 3903bd14e258d4..2a941d8bf505b8 100644 --- a/homeassistant/components/mailgun/__init__.py +++ b/homeassistant/components/mailgun/__init__.py @@ -88,6 +88,11 @@ async def async_unload_entry(hass, entry): hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'Mailgun Webhook', diff --git a/homeassistant/components/twilio/__init__.py b/homeassistant/components/twilio/__init__.py index ce8c272165f3f3..e7ba06a05f7d3b 100644 --- a/homeassistant/components/twilio/__init__.py +++ b/homeassistant/components/twilio/__init__.py @@ -60,6 +60,11 @@ async def async_unload_entry(hass, entry): hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID]) return True + +# pylint: disable=invalid-name +async_remove_entry = config_entry_flow.webhook_async_remove_entry + + config_entry_flow.register_webhook_flow( DOMAIN, 'Twilio Webhook', diff --git a/homeassistant/helpers/config_entry_flow.py b/homeassistant/helpers/config_entry_flow.py index 8f5705bc67a0bf..6d200a39c85948 100644 --- a/homeassistant/helpers/config_entry_flow.py +++ b/homeassistant/helpers/config_entry_flow.py @@ -118,15 +118,35 @@ async def async_step_user(self, user_input=None): ) webhook_id = self.hass.components.webhook.async_generate_id() - webhook_url = \ - self.hass.components.webhook.async_generate_url(webhook_id) + + if self.hass.components.cloud.async_active_subscription(): + webhook_url = \ + await self.hass.components.cloud.async_create_cloudhook( + webhook_id + ) + cloudhook = True + else: + webhook_url = \ + self.hass.components.webhook.async_generate_url(webhook_id) + cloudhook = False self._description_placeholder['webhook_url'] = webhook_url return self.async_create_entry( title=self._title, data={ - 'webhook_id': webhook_id + 'webhook_id': webhook_id, + 'cloudhook': cloudhook, }, description_placeholders=self._description_placeholder ) + + +async def webhook_async_remove_entry(hass, entry) -> None: + """Remove a webhook config entry.""" + if (not entry.data.get('cloudhook') or + 'cloud' not in hass.config.components): + return + + await hass.components.cloud.async_delete_cloudhook( + entry.data['webhook_id']) diff --git a/tests/helpers/test_config_entry_flow.py b/tests/helpers/test_config_entry_flow.py index 846c2cd15606d0..c198325b350499 100644 --- a/tests/helpers/test_config_entry_flow.py +++ b/tests/helpers/test_config_entry_flow.py @@ -3,9 +3,9 @@ import pytest -from homeassistant import config_entries, data_entry_flow, loader +from homeassistant import config_entries, data_entry_flow, loader, setup from homeassistant.helpers import config_entry_flow -from tests.common import MockConfigEntry, MockModule +from tests.common import MockConfigEntry, MockModule, mock_coro @pytest.fixture @@ -193,3 +193,51 @@ async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf): assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['data']['webhook_id'] is not None + + +async def test_webhook_create_cloudhook(hass, webhook_flow_conf): + """Test only a single entry is allowed.""" + assert await setup.async_setup_component(hass, 'cloud', {}) + + async_setup_entry = Mock(return_value=mock_coro(True)) + async_unload_entry = Mock(return_value=mock_coro(True)) + + loader.set_component(hass, 'test_single', MockModule( + 'test_single', + async_setup_entry=async_setup_entry, + async_unload_entry=async_unload_entry, + async_remove_entry=config_entry_flow.webhook_async_remove_entry, + )) + + result = await hass.config_entries.flow.async_init( + 'test_single', context={'source': config_entries.SOURCE_USER}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + + coro = mock_coro({ + 'cloudhook_url': 'https://example.com' + }) + + with patch('hass_nabucasa.cloudhooks.Cloudhooks.async_create', + return_value=coro) as mock_create, \ + patch('homeassistant.components.cloud.async_active_subscription', + return_value=True), \ + patch('homeassistant.components.cloud.async_is_logged_in', + return_value=True): + + result = await hass.config_entries.flow.async_configure( + result['flow_id'], {}) + + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['description_placeholders']['webhook_url'] == \ + 'https://example.com' + assert len(mock_create.mock_calls) == 1 + assert len(async_setup_entry.mock_calls) == 1 + + with patch('hass_nabucasa.cloudhooks.Cloudhooks.async_delete', + return_value=coro) as mock_delete: + + result = \ + await hass.config_entries.async_remove(result['result'].entry_id) + + assert len(mock_delete.mock_calls) == 1 + assert result['require_restart'] is False From 6829ecad9d044311dca8f1c02ebbad3e04e00320 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 1 Apr 2019 14:16:16 +0200 Subject: [PATCH 319/605] Hass.io ingress (#22505) * Fix API stream of snapshot / Add ingress * fix lint * Fix stream handling * Cleanup api handling * fix typing * Set proxy header * Use header constant * Enable the ingress setup * fix lint * Fix name * Fix tests * fix lint * forward params * Add tests for ingress * Cleanup cookie handling with aiohttp 3.5 * Add more tests * Fix tests * Fix lint * Fix header handling for steam * forward header too * fix lint * fix flake --- homeassistant/components/hassio/__init__.py | 4 + homeassistant/components/hassio/const.py | 7 +- homeassistant/components/hassio/http.py | 79 +++++--- homeassistant/components/hassio/ingress.py | 210 ++++++++++++++++++++ tests/components/hassio/test_http.py | 86 +++----- tests/components/hassio/test_ingress.py | 162 +++++++++++++++ tests/components/hassio/test_init.py | 2 +- tests/test_util/aiohttp.py | 2 +- 8 files changed, 461 insertions(+), 91 deletions(-) create mode 100644 homeassistant/components/hassio/ingress.py create mode 100644 tests/components/hassio/test_ingress.py diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 073974200a0935..e8d04b1596d980 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -20,6 +20,7 @@ from .discovery import async_setup_discovery from .handler import HassIO, HassioAPIError from .http import HassIOView +from .ingress import async_setup_ingress _LOGGER = logging.getLogger(__name__) @@ -270,4 +271,7 @@ async def async_handle_core_service(call): # Init auth Hass.io feature async_setup_auth(hass) + # Init ingress Hass.io feature + async_setup_ingress(hass, host) + return True diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index 964f94bfb41c63..e4132562c31e39 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -9,6 +9,7 @@ ATTR_USERNAME = 'username' ATTR_PASSWORD = 'password' -X_HASSIO = 'X-HASSIO-KEY' -X_HASS_USER_ID = 'X-HASS-USER-ID' -X_HASS_IS_ADMIN = 'X-HASS-IS-ADMIN' +X_HASSIO = 'X-Hassio-Key' +X_INGRESS_PATH = "X-Ingress-Path" +X_HASS_USER_ID = 'X-Hass-User-ID' +X_HASS_IS_ADMIN = 'X-Hass-Is-Admin' diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index 01ded9ca11dc75..7284004d72f9d4 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -3,10 +3,11 @@ import logging import os import re +from typing import Dict, Union import aiohttp from aiohttp import web -from aiohttp.hdrs import CONTENT_TYPE +from aiohttp.hdrs import CONTENT_TYPE, CONTENT_LENGTH from aiohttp.web_exceptions import HTTPBadGateway import async_timeout @@ -20,7 +21,8 @@ NO_TIMEOUT = re.compile( r'^(?:' r'|homeassistant/update' - r'|host/update' + r'|hassos/update' + r'|hassos/update/cli' r'|supervisor/update' r'|addons/[^/]+/(?:update|install|rebuild)' r'|snapshots/.+/full' @@ -44,25 +46,26 @@ class HassIOView(HomeAssistantView): url = "/api/hassio/{path:.+}" requires_auth = False - def __init__(self, host, websession): + def __init__(self, host: str, websession: aiohttp.ClientSession): """Initialize a Hass.io base view.""" self._host = host self._websession = websession - async def _handle(self, request, path): + async def _handle( + self, request: web.Request, path: str + ) -> Union[web.Response, web.StreamResponse]: """Route data to Hass.io.""" if _need_auth(path) and not request[KEY_AUTHENTICATED]: return web.Response(status=401) - client = await self._command_proxy(path, request) - - data = await client.read() - return _create_response(client, data) + return await self._command_proxy(path, request) get = _handle post = _handle - async def _command_proxy(self, path, request): + async def _command_proxy( + self, path: str, request: web.Request + ) -> Union[web.Response, web.StreamResponse]: """Return a client request with proxy origin for Hass.io supervisor. This method is a coroutine. @@ -71,29 +74,38 @@ async def _command_proxy(self, path, request): hass = request.app['hass'] data = None - headers = { - X_HASSIO: os.environ.get('HASSIO_TOKEN', ""), - } - user = request.get('hass_user') - if user is not None: - headers[X_HASS_USER_ID] = request['hass_user'].id - headers[X_HASS_IS_ADMIN] = str(int(request['hass_user'].is_admin)) + headers = _init_header(request) try: with async_timeout.timeout(10, loop=hass.loop): data = await request.read() - if data: - headers[CONTENT_TYPE] = request.content_type - else: - data = None method = getattr(self._websession, request.method.lower()) client = await method( "http://{}/{}".format(self._host, path), data=data, headers=headers, timeout=read_timeout ) + print(client.headers) + + # Simple request + if int(client.headers.get(CONTENT_LENGTH, 0)) < 4194000: + # Return Response + body = await client.read() + return web.Response( + content_type=client.content_type, + status=client.status, + body=body, + ) + + # Stream response + response = web.StreamResponse(status=client.status) + response.content_type = client.content_type - return client + await response.prepare(request) + async for data in client.content.iter_chunked(4096): + await response.write(data) + + return response except aiohttp.ClientError as err: _LOGGER.error("Client error on api %s request %s", path, err) @@ -104,23 +116,30 @@ async def _command_proxy(self, path, request): raise HTTPBadGateway() -def _create_response(client, data): - """Convert a response from client request.""" - return web.Response( - body=data, - status=client.status, - content_type=client.content_type, - ) +def _init_header(request: web.Request) -> Dict[str, str]: + """Create initial header.""" + headers = { + X_HASSIO: os.environ.get('HASSIO_TOKEN', ""), + CONTENT_TYPE: request.content_type, + } + + # Add user data + user = request.get('hass_user') + if user is not None: + headers[X_HASS_USER_ID] = request['hass_user'].id + headers[X_HASS_IS_ADMIN] = str(int(request['hass_user'].is_admin)) + + return headers -def _get_timeout(path): +def _get_timeout(path: str) -> int: """Return timeout for a URL path.""" if NO_TIMEOUT.match(path): return 0 return 300 -def _need_auth(path): +def _need_auth(path: str) -> bool: """Return if a path need authentication.""" if NO_AUTH.match(path): return False diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py new file mode 100644 index 00000000000000..6c1ef389712dff --- /dev/null +++ b/homeassistant/components/hassio/ingress.py @@ -0,0 +1,210 @@ +"""Hass.io Add-on ingress service.""" +import asyncio +from ipaddress import ip_address +import os +from typing import Dict, Union + +import aiohttp +from aiohttp import web +from aiohttp import hdrs +from aiohttp.web_exceptions import HTTPBadGateway +from multidict import CIMultiDict + +from homeassistant.core import callback +from homeassistant.components.http import HomeAssistantView +from homeassistant.helpers.typing import HomeAssistantType + +from .const import X_HASSIO, X_INGRESS_PATH + + +@callback +def async_setup_ingress(hass: HomeAssistantType, host: str): + """Auth setup.""" + websession = hass.helpers.aiohttp_client.async_get_clientsession() + + hassio_ingress = HassIOIngress(host, websession) + hass.http.register_view(hassio_ingress) + + +class HassIOIngress(HomeAssistantView): + """Hass.io view to handle base part.""" + + name = "api:hassio:ingress" + url = "/api/hassio_ingress/{addon}/{path:.+}" + requires_auth = False + + def __init__(self, host: str, websession: aiohttp.ClientSession): + """Initialize a Hass.io ingress view.""" + self._host = host + self._websession = websession + + def _create_url(self, addon: str, path: str) -> str: + """Create URL to service.""" + return "http://{}/addons/{}/web/{}".format(self._host, addon, path) + + async def _handle( + self, request: web.Request, addon: str, path: str + ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: + """Route data to Hass.io ingress service.""" + try: + # Websocket + if _is_websocket(request): + return await self._handle_websocket(request, addon, path) + + # Request + return await self._handle_request(request, addon, path) + + except aiohttp.ClientError: + pass + + raise HTTPBadGateway() from None + + get = _handle + post = _handle + put = _handle + delete = _handle + + async def _handle_websocket( + self, request: web.Request, addon: str, path: str + ) -> web.WebSocketResponse: + """Ingress route for websocket.""" + ws_server = web.WebSocketResponse() + await ws_server.prepare(request) + + url = self._create_url(addon, path) + source_header = _init_header(request, addon) + + # Start proxy + async with self._websession.ws_connect( + url, headers=source_header + ) as ws_client: + # Proxy requests + await asyncio.wait( + [ + _websocket_forward(ws_server, ws_client), + _websocket_forward(ws_client, ws_server), + ], + return_when=asyncio.FIRST_COMPLETED + ) + + return ws_server + + async def _handle_request( + self, request: web.Request, addon: str, path: str + ) -> Union[web.Response, web.StreamResponse]: + """Ingress route for request.""" + url = self._create_url(addon, path) + data = await request.read() + source_header = _init_header(request, addon) + + async with self._websession.request( + request.method, url, headers=source_header, + params=request.query, data=data, cookies=request.cookies + ) as result: + headers = _response_header(result) + + # Simple request + if hdrs.CONTENT_LENGTH in result.headers and \ + int(result.headers.get(hdrs.CONTENT_LENGTH, 0)) < 4194000: + # Return Response + body = await result.read() + return web.Response( + headers=headers, + status=result.status, + body=body + ) + + # Stream response + response = web.StreamResponse( + status=result.status, headers=headers) + response.content_type = result.content_type + + try: + await response.prepare(request) + async for data in result.content: + await response.write(data) + + except (aiohttp.ClientError, aiohttp.ClientPayloadError): + pass + + return response + + +def _init_header( + request: web.Request, addon: str +) -> Union[CIMultiDict, Dict[str, str]]: + """Create initial header.""" + headers = {} + + # filter flags + for name, value in request.headers.items(): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE): + continue + headers[name] = value + + # Inject token / cleanup later on Supervisor + headers[X_HASSIO] = os.environ.get('HASSIO_TOKEN', "") + + # Ingress information + headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(addon) + + # Set X-Forwarded-For + forward_for = request.headers.get(hdrs.X_FORWARDED_FOR) + connected_ip = ip_address(request.transport.get_extra_info('peername')[0]) + if forward_for: + forward_for = "{}, {!s}".format(forward_for, connected_ip) + else: + forward_for = "{!s}".format(connected_ip) + headers[hdrs.X_FORWARDED_FOR] = forward_for + + # Set X-Forwarded-Host + forward_host = request.headers.get(hdrs.X_FORWARDED_HOST) + if not forward_host: + forward_host = request.host + headers[hdrs.X_FORWARDED_HOST] = forward_host + + # Set X-Forwarded-Proto + forward_proto = request.headers.get(hdrs.X_FORWARDED_PROTO) + if not forward_proto: + forward_proto = request.url.scheme + headers[hdrs.X_FORWARDED_PROTO] = forward_proto + + return headers + + +def _response_header(response: aiohttp.ClientResponse) -> Dict[str, str]: + """Create response header.""" + headers = {} + + for name, value in response.headers.items(): + if name in (hdrs.TRANSFER_ENCODING, hdrs.CONTENT_LENGTH, + hdrs.CONTENT_TYPE): + continue + headers[name] = value + + return headers + + +def _is_websocket(request: web.Request) -> bool: + """Return True if request is a websocket.""" + headers = request.headers + + if headers.get(hdrs.CONNECTION) == "Upgrade" and \ + headers.get(hdrs.UPGRADE) == "websocket": + return True + return False + + +async def _websocket_forward(ws_from, ws_to): + """Handle websocket message directly.""" + async for msg in ws_from: + if msg.type == aiohttp.WSMsgType.TEXT: + await ws_to.send_str(msg.data) + elif msg.type == aiohttp.WSMsgType.BINARY: + await ws_to.send_bytes(msg.data) + elif msg.type == aiohttp.WSMsgType.PING: + await ws_to.ping() + elif msg.type == aiohttp.WSMsgType.PONG: + await ws_to.pong() + elif ws_to.closed: + await ws_to.close(code=ws_to.close_code, message=msg.extra) diff --git a/tests/components/hassio/test_http.py b/tests/components/hassio/test_http.py index 3f58c6e697e3b0..3a58048735bf97 100644 --- a/tests/components/hassio/test_http.py +++ b/tests/components/hassio/test_http.py @@ -1,29 +1,22 @@ """The tests for the hassio component.""" import asyncio -from unittest.mock import patch, Mock, MagicMock +from unittest.mock import patch import pytest from homeassistant.const import HTTP_HEADER_HA_AUTH -from tests.common import mock_coro from . import API_PASSWORD @asyncio.coroutine -def test_forward_request(hassio_client): +def test_forward_request(hassio_client, aioclient_mock): """Test fetching normal path.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http' - '._create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.post('/api/hassio/beer', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) + aioclient_mock.post("http://127.0.0.1/beer", text="response") + + resp = yield from hassio_client.post('/api/hassio/beer', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) # Check we got right response assert resp.status == 200 @@ -31,8 +24,7 @@ def test_forward_request(hassio_client): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine @@ -55,18 +47,13 @@ def test_auth_required_forward_request(hassio_noauth_client, build_type): 'app/index.html', 'app/hassio-app.html', 'app/index.html', 'app/hassio-app.html', 'app/some-chunk.js', 'app/app.js', ]) -def test_forward_request_no_auth_for_panel(hassio_client, build_type): +def test_forward_request_no_auth_for_panel( + hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get( - '/api/hassio/{}'.format(build_type)) + aioclient_mock.get( + "http://127.0.0.1/{}".format(build_type), text="response") + + resp = yield from hassio_client.get('/api/hassio/{}'.format(build_type)) # Check we got right response assert resp.status == 200 @@ -74,22 +61,16 @@ def test_forward_request_no_auth_for_panel(hassio_client, build_type): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine -def test_forward_request_no_auth_for_logo(hassio_client): +def test_forward_request_no_auth_for_logo(hassio_client, aioclient_mock): """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') + aioclient_mock.get( + "http://127.0.0.1/addons/bl_b392/logo", text="response") - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') + resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') # Check we got right response assert resp.status == 200 @@ -97,24 +78,18 @@ def test_forward_request_no_auth_for_logo(hassio_client): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine -def test_forward_log_request(hassio_client): +def test_forward_log_request(hassio_client, aioclient_mock): """Test fetching normal log path doesn't remove ANSI color escape codes.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = '\033[32mresponse\033[0m' - resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) + aioclient_mock.get( + "http://127.0.0.1/beer/logs", text="\033[32mresponse\033[0m") + + resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) # Check we got right response assert resp.status == 200 @@ -122,8 +97,7 @@ def test_forward_log_request(hassio_client): assert body == '\033[32mresponse\033[0m' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine @@ -151,5 +125,5 @@ async def test_forwarding_user_info(hassio_client, hass_admin_user, assert len(aioclient_mock.mock_calls) == 1 req_headers = aioclient_mock.mock_calls[0][-1] - req_headers['X-HASS-USER-ID'] == hass_admin_user.id - req_headers['X-HASS-IS-ADMIN'] == '1' + req_headers['X-Hass-User-ID'] == hass_admin_user.id + req_headers['X-Hass-Is-Admin'] == '1' diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py new file mode 100644 index 00000000000000..4e071ba24fd897 --- /dev/null +++ b/tests/components/hassio/test_ingress.py @@ -0,0 +1,162 @@ +"""The tests for the hassio component.""" + +from aiohttp.hdrs import X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO +from aiohttp.client_exceptions import WSServerHandshakeError +import pytest + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_get( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.get( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_post( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.post("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.post( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_put( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.put("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.put( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_delete( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.delete("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.delete( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ws"), ("core", "ws.php"), + ("local", "panel/config/stream"), ("jk_921", "hulk") + ]) +async def test_ingress_websocket( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1])) + + # Ignore error because we can setup a full IO infrastructure + with pytest.raises(WSServerHandshakeError): + await hassio_client.ws_connect( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index fc4661e7544bd9..f1f148f8495c1f 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -207,7 +207,7 @@ def test_setup_hassio_no_additional_data(hass, aioclient_mock): assert result assert aioclient_mock.call_count == 3 - assert aioclient_mock.mock_calls[-1][3]['X-HASSIO-KEY'] == "123456" + assert aioclient_mock.mock_calls[-1][3]['X-Hassio-Key'] == "123456" @asyncio.coroutine diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index 8b3b057bfc0d84..ab759f03058f1e 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -102,7 +102,7 @@ def create_session(self, loop): async def match_request(self, method, url, *, data=None, auth=None, params=None, headers=None, allow_redirects=None, - timeout=None, json=None): + timeout=None, json=None, cookies=None): """Match a request against pre-registered requests.""" data = data or json url = URL(url) From de4d1f2c19de6a6eb2375aaf708b45f0c7133e25 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 07:12:59 -0700 Subject: [PATCH 320/605] Config CircleCI workflow (#22590) * Add mypyrc to control typing check, add mypy to circle * Add translation upload circlci job --- .circleci/config.yml | 47 ++++++++++++++++++++++++++++++-------- README.rst | 6 +++-- mypyrc | 21 +++++++++++++++++ script/translations_upload | 3 ++- tox.ini | 2 +- 5 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 mypyrc diff --git a/.circleci/config.yml b/.circleci/config.yml index f9eb28bdf4aa86..b4f22601bb5c8a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ commands: command: | python3 -m venv venv . venv/bin/activate - pip install -U pip + pip install -q -U pip <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> @@ -68,28 +68,35 @@ commands: name: install command: | . venv/bin/activate - pip install --progress-bar off -e . + pip install -q --progress-bar off -e . jobs: static-check: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch steps: - checkout - docker-prereqs + - install-requirements: + python: 3.5.5-stretch + test: true - run: name: run static check command: | - python3 -m venv venv . venv/bin/activate - pip install -U pip - pip install --progress-bar off flake8 flake8 + - run: + name: run static type check + command: | + . venv/bin/activate + TYPING_FILES=$(cat mypyrc) + mypy $TYPING_FILES + - install - run: name: run gen_requirements_all @@ -114,7 +121,7 @@ jobs: executor: name: python tag: 3.7-stretch - parallelism: 3 + parallelism: 2 steps: - checkout @@ -154,7 +161,7 @@ jobs: executor: name: python tag: << parameters.python >> - parallelism: 3 + parallelism: 2 steps: - checkout @@ -172,7 +179,6 @@ jobs: if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty - when: always - store_test_results: path: test-reports @@ -185,6 +191,23 @@ jobs: path: test-reports destination: test-reports + # This job use machine executor, e.g. classic CircleCI VM because we need both lokalise-cli and a Python runtime. + # Classic CircleCI included python 2.7.12 and python 3.5.2 managed by pyenv, the Python version may need change if + # CircleCI changed its VM in future. + upload-translations: + machine: true + + steps: + - checkout + + - run: + name: upload english translations + command: | + pyenv versions + pyenv global 3.5.2 + docker pull lokalise/lokalise-cli@sha256:2198814ebddfda56ee041a4b427521757dd57f75415ea9693696a64c550cef21 + script/translations_upload + workflows: version: 2 build: @@ -222,3 +245,9 @@ workflows: # - test: # name: test 3.8 # python: 3.8-rc-stretch + - upload-translations: + requires: + - static-check + filters: + branches: + only: dev diff --git a/README.rst b/README.rst index f231d6c5514c94..941a463fb37410 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -Home Assistant |Build Status| |Coverage Status| |Chat Status| +Home Assistant |Build Status| |CI Status| |Coverage Status| |Chat Status| ================================================================================= Home Assistant is a home automation platform running on Python 3. It is able to track and control all devices at home and offer a platform for automating control. @@ -27,8 +27,10 @@ components `__ of our website for further help and information. -.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master +.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=dev :target: https://travis-ci.org/home-assistant/home-assistant +.. |CI Status| image:: https://circleci.com/gh/home-assistant/home-assistant.svg?style=shield + :target: https://circleci.com/gh/home-assistant/home-assistant .. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg :target: https://coveralls.io/r/home-assistant/home-assistant?branch=master .. |Chat Status| image:: https://img.shields.io/discord/330944238910963714.svg diff --git a/mypyrc b/mypyrc new file mode 100644 index 00000000000000..7c73d12e3815a0 --- /dev/null +++ b/mypyrc @@ -0,0 +1,21 @@ +homeassistant/*.py +homeassistant/auth/ +homeassistant/util/ +homeassistant/helpers/__init__.py +homeassistant/helpers/aiohttp_client.py +homeassistant/helpers/area_registry.py +homeassistant/helpers/condition.py +homeassistant/helpers/deprecation.py +homeassistant/helpers/dispatcher.py +homeassistant/helpers/entity_values.py +homeassistant/helpers/entityfilter.py +homeassistant/helpers/icon.py +homeassistant/helpers/intent.py +homeassistant/helpers/json.py +homeassistant/helpers/location.py +homeassistant/helpers/signal.py +homeassistant/helpers/state.py +homeassistant/helpers/sun.py +homeassistant/helpers/temperature.py +homeassistant/helpers/translation.py +homeassistant/helpers/typing.py diff --git a/script/translations_upload b/script/translations_upload index 5bf9fe1e1217cb..52045e41d60c81 100755 --- a/script/translations_upload +++ b/script/translations_upload @@ -26,7 +26,8 @@ LANG_ISO=en CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) -if [ "${CURRENT_BRANCH-}" != "dev" ] && [ "${TRAVIS_BRANCH-}" != "dev" ] ; then +# Check Travis and CircleCI environment as well +if [ "${CURRENT_BRANCH-}" != "dev" ] && [ "${TRAVIS_BRANCH-}" != "dev" ] && [ "${CIRCLE_BRANCH-}" != "dev" ]; then echo "Please only run the translations upload script from a clean checkout of dev." exit 1 fi diff --git a/tox.ini b/tox.ini index 8423141df60819..b8995d9e877b94 100644 --- a/tox.ini +++ b/tox.ini @@ -42,4 +42,4 @@ deps = -r{toxinidir}/requirements_test.txt -c{toxinidir}/homeassistant/package_constraints.txt commands = - /bin/bash -c 'mypy homeassistant/*.py homeassistant/{auth,util}/ homeassistant/helpers/{__init__,aiohttp_client,area_registry,condition,deprecation,dispatcher,entity_values,entityfilter,icon,intent,json,location,signal,state,sun,temperature,translation,typing}.py' + /bin/bash -c 'TYPING_FILES=$(cat mypyrc); mypy $TYPING_FILES' From a5b03541e90a7ff7aff9f4b26573879c9c1de3fa Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 1 Apr 2019 17:16:56 +0200 Subject: [PATCH 321/605] Delete .travis.yml --- .travis.yml | 55 ----------------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0461d182232323..00000000000000 --- a/.travis.yml +++ /dev/null @@ -1,55 +0,0 @@ -sudo: false -dist: xenial -addons: - apt: - sources: - - sourceline: "ppa:jonathonf/ffmpeg-4" - packages: - - libudev-dev - - libavformat-dev - - libavcodec-dev - - libavdevice-dev - - libavutil-dev - - libswscale-dev - - libswresample-dev - - libavfilter-dev -matrix: - fast_finish: true - include: - - python: "3.5.3" - env: TOXENV=lint - - python: "3.5.3" - env: TOXENV=pylint - - python: "3.5.3" - env: TOXENV=typing - - python: "3.5.3" - env: TOXENV=cov - after_success: coveralls - - python: "3.6" - env: TOXENV=py36 - - python: "3.7" - env: TOXENV=py37 - - python: "3.8-dev" - env: TOXENV=py38 - if: branch = dev AND type = push - allow_failures: - - python: "3.8-dev" - env: TOXENV=py38 - -cache: - directories: - - $HOME/.cache/pip -install: pip install -U tox coveralls -language: python -script: travis_wait 40 tox --develop -services: - - docker -before_deploy: - - docker pull lokalise/lokalise-cli@sha256:2198814ebddfda56ee041a4b427521757dd57f75415ea9693696a64c550cef21 -deploy: - skip_cleanup: true - provider: script - script: script/travis_deploy - on: - branch: dev - condition: $TOXENV = lint From 9f2c5b7231bcf26e4d5052d01187a65c08fcae19 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Mon, 1 Apr 2019 11:58:52 -0500 Subject: [PATCH 322/605] Add source selection to Heos component (#22592) * Add select source support * Review feedback changes * Removed unused import * Ignore 'umused' import used in typing * Only include trace back on useful errors * Remove return from play_source --- homeassistant/components/heos/__init__.py | 137 ++++++++++++++++-- homeassistant/components/heos/const.py | 4 + homeassistant/components/heos/media_player.py | 47 +++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/heos/conftest.py | 46 +++++- tests/components/heos/test_init.py | 55 +++++-- tests/components/heos/test_media_player.py | 120 +++++++++++++-- 8 files changed, 365 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index 2214a602ef331f..dadd9f1046445d 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -1,5 +1,6 @@ """Denon HEOS Media Player.""" import asyncio +from datetime import timedelta import logging import voluptuous as vol @@ -8,13 +9,17 @@ DOMAIN as MEDIA_PLAYER_DOMAIN) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP +from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from homeassistant.util import Throttle from .config_flow import format_title -from .const import DATA_CONTROLLER, DOMAIN +from .const import ( + COMMAND_RETRY_ATTEMPTS, COMMAND_RETRY_DELAY, DATA_CONTROLLER, + DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) -REQUIREMENTS = ['pyheos==0.2.0'] +REQUIREMENTS = ['pyheos==0.3.0'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -22,6 +27,8 @@ }) }, extra=vol.ALLOW_EXTRA) +MIN_UPDATE_SOURCES = timedelta(seconds=1) + _LOGGER = logging.getLogger(__name__) @@ -50,7 +57,7 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): """Initialize config entry which represents the HEOS controller.""" - from pyheos import Heos + from pyheos import Heos, CommandError host = entry.data[CONF_HOST] # Setting all_progress_events=False ensures that we only receive a # media position update upon start of playback or when media changes @@ -58,26 +65,34 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): try: await controller.connect(auto_reconnect=True) # Auto reconnect only operates if initial connection was successful. - except (asyncio.TimeoutError, ConnectionError) as error: + except (asyncio.TimeoutError, ConnectionError, CommandError) as error: await controller.disconnect() - _LOGGER.exception("Unable to connect to controller %s: %s", - host, type(error).__name__) - return False + _LOGGER.debug("Unable to connect to controller %s: %s", host, error) + raise ConfigEntryNotReady + # Disconnect when shutting down async def disconnect_controller(event): await controller.disconnect() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) try: - players = await controller.get_players() - except (asyncio.TimeoutError, ConnectionError) as error: + players, favorites, inputs = await asyncio.gather( + controller.get_players(), + controller.get_favorites(), + controller.get_input_sources() + ) + except (asyncio.TimeoutError, ConnectionError, CommandError) as error: await controller.disconnect() - _LOGGER.exception("Unable to retrieve players: %s", - type(error).__name__) - return False + _LOGGER.debug("Unable to retrieve players and sources: %s", error, + exc_info=isinstance(error, CommandError)) + raise ConfigEntryNotReady + + source_manager = SourceManager(favorites, inputs) + source_manager.connect_update(hass, controller) hass.data[DOMAIN] = { DATA_CONTROLLER: controller, + DATA_SOURCE_MANAGER: source_manager, MEDIA_PLAYER_DOMAIN: players } hass.async_create_task(hass.config_entries.async_forward_entry_setup( @@ -88,7 +103,105 @@ async def disconnect_controller(event): async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry): """Unload a config entry.""" controller = hass.data[DOMAIN][DATA_CONTROLLER] + controller.dispatcher.disconnect_all() await controller.disconnect() hass.data.pop(DOMAIN) return await hass.config_entries.async_forward_entry_unload( entry, MEDIA_PLAYER_DOMAIN) + + +class SourceManager: + """Class that manages sources for players.""" + + def __init__(self, favorites, inputs, *, + retry_delay: int = COMMAND_RETRY_DELAY, + max_retry_attempts: int = COMMAND_RETRY_ATTEMPTS): + """Init input manager.""" + self.retry_delay = retry_delay + self.max_retry_attempts = max_retry_attempts + self.favorites = favorites + self.inputs = inputs + self.source_list = self._build_source_list() + + def _build_source_list(self): + """Build a single list of inputs from various types.""" + source_list = [] + source_list.extend([favorite.name for favorite + in self.favorites.values()]) + source_list.extend([source.name for source in self.inputs]) + return source_list + + async def play_source(self, source: str, player): + """Determine type of source and play it.""" + index = next((index for index, favorite in self.favorites.items() + if favorite.name == source), None) + if index is not None: + await player.play_favorite(index) + return + + input_source = next((input_source for input_source in self.inputs + if input_source.name == source), None) + if input_source is not None: + await player.play_input_source(input_source) + return + + _LOGGER.error("Unknown source: %s", source) + + def get_current_source(self, now_playing_media): + """Determine current source from now playing media.""" + from pyheos import const + # Match input by input_name:media_id + if now_playing_media.source_id == const.MUSIC_SOURCE_AUX_INPUT: + return next((input_source.name for input_source in self.inputs + if input_source.input_name == + now_playing_media.media_id), None) + # Try matching favorite by name:station or media_id:album_id + return next((source.name for source in self.favorites.values() + if source.name == now_playing_media.station + or source.media_id == now_playing_media.album_id), None) + + def connect_update(self, hass, controller): + """ + Connect listener for when sources change and signal player update. + + EVENT_SOURCES_CHANGED is often raised multiple times in response to a + physical event therefore throttle it. Retrieving sources immediately + after the event may fail so retry. + """ + from pyheos import CommandError, const + + @Throttle(MIN_UPDATE_SOURCES) + async def get_sources(): + retry_attempts = 0 + while True: + try: + return await asyncio.gather( + controller.get_favorites(), + controller.get_input_sources()) + except (asyncio.TimeoutError, ConnectionError, CommandError) \ + as error: + if retry_attempts < self.max_retry_attempts: + retry_attempts += 1 + _LOGGER.debug("Error retrieving sources and will " + "retry: %s", error, + exc_info=isinstance(error, CommandError)) + await asyncio.sleep(self.retry_delay) + else: + _LOGGER.error("Unable to update sources: %s", error, + exc_info=isinstance(error, CommandError)) + return + + async def update_sources(event): + if event in const.EVENT_SOURCES_CHANGED: + sources = await get_sources() + # If throttled, it will return None + if sources: + self.favorites, self.inputs = sources + self.source_list = self._build_source_list() + _LOGGER.debug("Sources updated due to changed event") + # Let players know to update + hass.helpers.dispatcher.async_dispatcher_send( + SIGNAL_HEOS_SOURCES_UPDATED) + + controller.dispatcher.connect( + const.SIGNAL_CONTROLLER_EVENT, update_sources) diff --git a/homeassistant/components/heos/const.py b/homeassistant/components/heos/const.py index 65c452e4a71f03..9cb65589b438e4 100644 --- a/homeassistant/components/heos/const.py +++ b/homeassistant/components/heos/const.py @@ -1,4 +1,8 @@ """Const for the HEOS integration.""" +COMMAND_RETRY_ATTEMPTS = 2 +COMMAND_RETRY_DELAY = 1 DATA_CONTROLLER = "controller" +DATA_SOURCE_MANAGER = "source_manager" DOMAIN = 'heos' +SIGNAL_HEOS_SOURCES_UPDATED = "heos_sources_updated" diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index f96435dc713541..466c9ae8faa938 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -1,22 +1,27 @@ """Denon HEOS Media Player.""" from functools import reduce from operator import ior +from typing import Sequence from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( DOMAIN, MEDIA_TYPE_MUSIC, SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, - SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_SHUFFLE_SET, - SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOURCE, + SUPPORT_SHUFFLE_SET, SUPPORT_STOP, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_STEP) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING +from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util.dt import utcnow -from .const import DOMAIN as HEOS_DOMAIN +from .const import ( + DATA_SOURCE_MANAGER, DOMAIN as HEOS_DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) DEPENDENCIES = ['heos'] BASE_SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ SUPPORT_VOLUME_STEP | SUPPORT_CLEAR_PLAYLIST | \ - SUPPORT_SHUFFLE_SET + SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOURCE async def async_setup_platform( @@ -25,8 +30,9 @@ async def async_setup_platform( pass -async def async_setup_entry(hass, config_entry, async_add_entities): - """Add binary sensors for a config entry.""" +async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry, + async_add_entities): + """Add media players for a config entry.""" players = hass.data[HEOS_DOMAIN][DOMAIN] devices = [HeosMediaPlayer(player) for player in players.values()] async_add_entities(devices, True) @@ -42,6 +48,7 @@ def __init__(self, player): self._player = player self._signals = [] self._supported_features = BASE_SUPPORTED_FEATURES + self._source_manager = None self._play_state_to_state = { const.PLAY_STATE_PLAY: STATE_PLAYING, const.PLAY_STATE_STOP: STATE_IDLE, @@ -74,9 +81,14 @@ async def _player_update(self, player_id, event): self._media_position_updated_at = utcnow() await self.async_update_ha_state(True) + async def _sources_updated(self): + """Handle sources changed.""" + await self.async_update_ha_state(True) + async def async_added_to_hass(self): """Device added to hass.""" from pyheos import const + self._source_manager = self.hass.data[HEOS_DOMAIN][DATA_SOURCE_MANAGER] # Update state when attributes of the player change self._signals.append(self._player.heos.dispatcher.connect( const.SIGNAL_PLAYER_EVENT, self._player_update)) @@ -86,6 +98,10 @@ async def async_added_to_hass(self): # Update state upon connect/disconnects self._signals.append(self._player.heos.dispatcher.connect( const.SIGNAL_HEOS_EVENT, self._heos_event)) + # Update state when sources change + self._signals.append( + self.hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_HEOS_SOURCES_UPDATED, self._sources_updated)) async def async_clear_playlist(self): """Clear players playlist.""" @@ -115,6 +131,10 @@ async def async_mute_volume(self, mute): """Mute the volume.""" await self._player.set_mute(mute) + async def async_select_source(self, source): + """Select input source.""" + await self._source_manager.play_source(source, self._player) + async def async_set_shuffle(self, shuffle): """Enable/disable shuffle mode.""" await self._player.set_play_mode(self._player.repeat, shuffle) @@ -218,7 +238,9 @@ def media_position_updated_at(self): @property def media_image_url(self) -> str: """Image url of current playing media.""" - return self._player.now_playing_media.image_url + # May be an empty string, if so, return None + image_url = self._player.now_playing_media.image_url + return image_url if image_url else None @property def media_title(self) -> str: @@ -240,6 +262,17 @@ def shuffle(self) -> bool: """Boolean if shuffle is enabled.""" return self._player.shuffle + @property + def source(self) -> str: + """Name of the current input source.""" + return self._source_manager.get_current_source( + self._player.now_playing_media) + + @property + def source_list(self) -> Sequence[str]: + """List of available input sources.""" + return self._source_manager.source_list + @property def state(self) -> str: """State of the player.""" diff --git a/requirements_all.txt b/requirements_all.txt index ef42a58e97cf42..5d9f175df5e130 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1077,7 +1077,7 @@ pygtt==1.1.2 pyhaversion==2.0.3 # homeassistant.components.heos -pyheos==0.2.0 +pyheos==0.3.0 # homeassistant.components.hikvision.binary_sensor pyhik==0.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 85b49f71ce70ea..bc51c45f84930c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -206,7 +206,7 @@ pydeconz==54 pydispatcher==2.0.5 # homeassistant.components.heos -pyheos==0.2.0 +pyheos==0.3.0 # homeassistant.components.homematic pyhomematic==0.1.58 diff --git a/tests/components/heos/conftest.py b/tests/components/heos/conftest.py index 6aa2f316088a6b..1b76db21187db3 100644 --- a/tests/components/heos/conftest.py +++ b/tests/components/heos/conftest.py @@ -1,6 +1,8 @@ """Configuration for HEOS tests.""" +from typing import Dict, Sequence + from asynctest.mock import Mock, patch as patch -from pyheos import Dispatcher, HeosPlayer, const +from pyheos import Dispatcher, HeosPlayer, HeosSource, InputSource, const import pytest from homeassistant.components.heos import DOMAIN @@ -17,12 +19,15 @@ def config_entry_fixture(): @pytest.fixture(name="controller") -def controller_fixture(players): +def controller_fixture(players, favorites, input_sources, dispatcher): """Create a mock Heos controller fixture.""" with patch("pyheos.Heos", autospec=True) as mock: mock_heos = mock.return_value + mock_heos.dispatcher = dispatcher mock_heos.get_players.return_value = players mock_heos.players = players + mock_heos.get_favorites.return_value = favorites + mock_heos.get_input_sources.return_value = input_sources yield mock_heos @@ -35,10 +40,10 @@ def config_fixture(): @pytest.fixture(name="players") -def player_fixture(): +def player_fixture(dispatcher): """Create a mock HeosPlayer.""" player = Mock(HeosPlayer, autospec=True) - player.heos.dispatcher = Dispatcher() + player.heos.dispatcher = dispatcher player.player_id = 1 player.name = "Test Player" player.model = "Test Model" @@ -65,3 +70,36 @@ def player_fixture(): player.now_playing_media.image_url = "http://" player.now_playing_media.song = "Song" return {player.player_id: player} + + +@pytest.fixture(name="favorites") +def favorites_fixture() -> Dict[int, HeosSource]: + """Create favorites fixture.""" + station = Mock(HeosSource, autospec=True) + station.type = const.TYPE_STATION + station.name = "Today's Hits Radio" + station.media_id = '123456789' + radio = Mock(HeosSource, autospec=True) + radio.type = const.TYPE_STATION + radio.name = "Classical MPR (Classical Music)" + radio.media_id = 's1234' + return { + 1: station, + 2: radio + } + + +@pytest.fixture(name="input_sources") +def input_sources_fixture() -> Sequence[InputSource]: + """Create a set of input sources for testing.""" + source = Mock(InputSource, autospec=True) + source.player_id = 1 + source.input_name = const.INPUT_AUX_IN_1 + source.name = "HEOS Drive - Line In 1" + return [source] + + +@pytest.fixture(name="dispatcher") +def dispatcher_fixture() -> Dispatcher: + """Create a dispatcher for testing.""" + return Dispatcher() diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py index b89c39113e4d2b..b6bc3e24e1ac96 100644 --- a/tests/components/heos/test_init.py +++ b/tests/components/heos/test_init.py @@ -2,12 +2,17 @@ import asyncio from asynctest import patch +from pyheos import CommandError, const +import pytest -from homeassistant.components.heos import async_setup_entry, async_unload_entry -from homeassistant.components.heos.const import DATA_CONTROLLER, DOMAIN +from homeassistant.components.heos import ( + SourceManager, async_setup_entry, async_unload_entry) +from homeassistant.components.heos.const import ( + DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN) from homeassistant.components.media_player.const import ( DOMAIN as MEDIA_PLAYER_DOMAIN) from homeassistant.const import CONF_HOST +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.setup import async_setup_component @@ -36,7 +41,7 @@ async def test_async_setup_updates_entry(hass, config_entry, config): async def test_async_setup_returns_true(hass, config_entry, config): - """Test component setup updates entry from config.""" + """Test component setup from config.""" config_entry.add_to_hass(hass) assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() @@ -46,7 +51,7 @@ async def test_async_setup_returns_true(hass, config_entry, config): async def test_async_setup_no_config_returns_true(hass, config_entry): - """Test component setup updates entry from entry only.""" + """Test component setup from entry only.""" config_entry.add_to_hass(hass) assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() @@ -67,21 +72,21 @@ async def test_async_setup_entry_loads_platforms( assert forward_mock.call_count == 1 assert controller.connect.call_count == 1 controller.disconnect.assert_not_called() - assert hass.data[DOMAIN] == { - DATA_CONTROLLER: controller, - MEDIA_PLAYER_DOMAIN: controller.players - } + assert hass.data[DOMAIN][DATA_CONTROLLER] == controller + assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players + assert isinstance(hass.data[DOMAIN][DATA_SOURCE_MANAGER], SourceManager) async def test_async_setup_entry_connect_failure( hass, config_entry, controller): - """Test failure to connect does not load entry.""" + """Connection failure raises ConfigEntryNotReady.""" config_entry.add_to_hass(hass) errors = [ConnectionError, asyncio.TimeoutError] for error in errors: controller.connect.side_effect = error - assert not await async_setup_entry(hass, config_entry) - await hass.async_block_till_done() + with pytest.raises(ConfigEntryNotReady): + await async_setup_entry(hass, config_entry) + await hass.async_block_till_done() assert controller.connect.call_count == 1 assert controller.disconnect.call_count == 1 controller.connect.reset_mock() @@ -90,13 +95,14 @@ async def test_async_setup_entry_connect_failure( async def test_async_setup_entry_player_failure( hass, config_entry, controller): - """Test failure to retrieve players does not load entry.""" + """Failure to retrieve players/sources raises ConfigEntryNotReady.""" config_entry.add_to_hass(hass) errors = [ConnectionError, asyncio.TimeoutError] for error in errors: controller.get_players.side_effect = error - assert not await async_setup_entry(hass, config_entry) - await hass.async_block_till_done() + with pytest.raises(ConfigEntryNotReady): + await async_setup_entry(hass, config_entry) + await hass.async_block_till_done() assert controller.connect.call_count == 1 assert controller.disconnect.call_count == 1 controller.connect.reset_mock() @@ -112,3 +118,24 @@ async def test_unload_entry(hass, config_entry, controller): await hass.async_block_till_done() assert controller.disconnect.call_count == 1 assert unload.call_count == 1 + assert DOMAIN not in hass.data + + +async def test_update_sources_retry(hass, config_entry, config, controller, + caplog): + """Test update sources retries on failures to max attempts.""" + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, config) + controller.get_favorites.reset_mock() + controller.get_input_sources.reset_mock() + source_manager = hass.data[DOMAIN][DATA_SOURCE_MANAGER] + source_manager.retry_delay = 0 + source_manager.max_retry_attempts = 1 + controller.get_favorites.side_effect = CommandError("Test", "test", 0) + controller.dispatcher.send( + const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) + # Wait until it's finished + while "Unable to update sources" not in caplog.text: + await asyncio.sleep(0.1) + assert controller.get_favorites.call_count == 2 + assert controller.get_input_sources.call_count == 2 diff --git a/tests/components/heos/test_media_player.py b/tests/components/heos/test_media_player.py index d065740f7c9926..2f8d7dfc1e93ca 100644 --- a/tests/components/heos/test_media_player.py +++ b/tests/components/heos/test_media_player.py @@ -1,16 +1,19 @@ """Tests for the Heos Media Player platform.""" +import asyncio + from pyheos import const from homeassistant.components.heos import media_player -from homeassistant.components.heos.const import DOMAIN +from homeassistant.components.heos.const import ( + DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) from homeassistant.components.media_player.const import ( - ATTR_MEDIA_ALBUM_NAME, ATTR_MEDIA_ARTIST, ATTR_MEDIA_CONTENT_ID, - ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_POSITION, - ATTR_MEDIA_POSITION_UPDATED_AT, ATTR_MEDIA_SHUFFLE, ATTR_MEDIA_TITLE, - ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, - DOMAIN as MEDIA_PLAYER_DOMAIN, MEDIA_TYPE_MUSIC, SERVICE_CLEAR_PLAYLIST, - SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, - SUPPORT_STOP) + ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_ALBUM_NAME, + ATTR_MEDIA_ARTIST, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, + ATTR_MEDIA_DURATION, ATTR_MEDIA_POSITION, ATTR_MEDIA_POSITION_UPDATED_AT, + ATTR_MEDIA_SHUFFLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_VOLUME_LEVEL, + ATTR_MEDIA_VOLUME_MUTED, DOMAIN as MEDIA_PLAYER_DOMAIN, MEDIA_TYPE_MUSIC, + SERVICE_CLEAR_PLAYLIST, SERVICE_SELECT_SOURCE, SUPPORT_NEXT_TRACK, + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_STOP) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_SUPPORTED_FEATURES, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, @@ -56,10 +59,13 @@ async def test_state_attributes(hass, config_entry, config, controller): assert state.attributes[ATTR_SUPPORTED_FEATURES] == \ SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_STOP | SUPPORT_NEXT_TRACK | \ SUPPORT_PREVIOUS_TRACK | media_player.BASE_SUPPORTED_FEATURES + assert ATTR_INPUT_SOURCE not in state.attributes + assert state.attributes[ATTR_INPUT_SOURCE_LIST] == \ + hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list async def test_updates_start_from_signals( - hass, config_entry, config, controller): + hass, config_entry, config, controller, favorites): """Tests dispatched signals update player.""" await setup_platform(hass, config_entry, config) player = controller.players[1] @@ -110,6 +116,23 @@ async def test_updates_start_from_signals( state = hass.states.get('media_player.test_player') assert state.state == STATE_PLAYING + # Test sources event update + event = asyncio.Event() + + async def set_signal(): + event.set() + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_HEOS_SOURCES_UPDATED, set_signal) + + favorites.clear() + player.heos.dispatcher.send( + const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) + await event.wait() + source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list + assert len(source_list) == 1 + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list + async def test_services(hass, config_entry, config, controller): """Tests player commands.""" @@ -173,6 +196,85 @@ async def test_services(hass, config_entry, config, controller): player.set_volume.assert_called_once_with(100) +async def test_select_favorite( + hass, config_entry, config, controller, favorites): + """Tests selecting a music service favorite and state.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + # Test set music service preset + favorite = favorites[1] + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_INPUT_SOURCE: favorite.name}, blocking=True) + player.play_favorite.assert_called_once_with(1) + # Test state is matched by station name + player.now_playing_media.station = favorite.name + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, player.player_id, + const.EVENT_PLAYER_STATE_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_INPUT_SOURCE] == favorite.name + + +async def test_select_radio_favorite( + hass, config_entry, config, controller, favorites): + """Tests selecting a radio favorite and state.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + # Test set radio preset + favorite = favorites[2] + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_INPUT_SOURCE: favorite.name}, blocking=True) + player.play_favorite.assert_called_once_with(2) + # Test state is matched by album id + player.now_playing_media.station = "Classical" + player.now_playing_media.album_id = favorite.media_id + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, player.player_id, + const.EVENT_PLAYER_STATE_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_INPUT_SOURCE] == favorite.name + + +async def test_select_input_source( + hass, config_entry, config, controller, input_sources): + """Tests selecting input source and state.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + # Test proper service called + input_source = input_sources[0] + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_INPUT_SOURCE: input_source.name}, blocking=True) + player.play_input_source.assert_called_once_with(input_source) + # Test state is matched by media id + player.now_playing_media.source_id = const.MUSIC_SOURCE_AUX_INPUT + player.now_playing_media.media_id = const.INPUT_AUX_IN_1 + player.heos.dispatcher.send( + const.SIGNAL_PLAYER_EVENT, player.player_id, + const.EVENT_PLAYER_STATE_CHANGED) + await hass.async_block_till_done() + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_INPUT_SOURCE] == input_source.name + + +async def test_select_input_unknown( + hass, config_entry, config, controller, caplog): + """Tests selecting an unknown input.""" + await setup_platform(hass, config_entry, config) + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, + {ATTR_ENTITY_ID: 'media_player.test_player', + ATTR_INPUT_SOURCE: "Unknown"}, blocking=True) + assert "Unknown source: Unknown" in caplog.text + + async def test_unload_config_entry(hass, config_entry, config, controller): """Test the player is removed when the config entry is unloaded.""" await setup_platform(hass, config_entry, config) From 1e96d696887577e2317857115777146611335347 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 1 Apr 2019 19:00:25 +0200 Subject: [PATCH 323/605] Update face_recognition to 1.2.3 (#22622) --- homeassistant/components/dlib_face_detect/image_processing.py | 2 +- homeassistant/components/dlib_face_identify/image_processing.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dlib_face_detect/image_processing.py b/homeassistant/components/dlib_face_detect/image_processing.py index cb9ea5ff5f996e..fea756395e427c 100644 --- a/homeassistant/components/dlib_face_detect/image_processing.py +++ b/homeassistant/components/dlib_face_detect/image_processing.py @@ -13,7 +13,7 @@ from homeassistant.components.image_processing import ( ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) -REQUIREMENTS = ['face_recognition==1.0.0'] +REQUIREMENTS = ['face_recognition==1.2.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dlib_face_identify/image_processing.py b/homeassistant/components/dlib_face_identify/image_processing.py index d8c3f5f621fc80..6611fb0edfbf95 100644 --- a/homeassistant/components/dlib_face_identify/image_processing.py +++ b/homeassistant/components/dlib_face_identify/image_processing.py @@ -15,7 +15,7 @@ CONF_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['face_recognition==1.0.0'] +REQUIREMENTS = ['face_recognition==1.2.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 5d9f175df5e130..3868e744228aeb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -418,7 +418,7 @@ evohomeclient==0.2.8 # homeassistant.components.dlib_face_detect.image_processing # homeassistant.components.dlib_face_identify.image_processing -# face_recognition==1.0.0 +# face_recognition==1.2.3 # homeassistant.components.fastdotcom fastdotcom==0.0.3 From 0056fcf904a2f1f340ce65c01bd862a27d6c74f8 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 1 Apr 2019 19:01:31 +0200 Subject: [PATCH 324/605] Make platform setup a coroutine (#22620) * make setup_platform a coroutine * Update homeassistant/components/tellduslive/sensor.py Co-Authored-By: fredrike --- homeassistant/components/daikin/climate.py | 3 ++- homeassistant/components/daikin/sensor.py | 3 ++- homeassistant/components/daikin/switch.py | 3 ++- homeassistant/components/tellduslive/binary_sensor.py | 3 ++- homeassistant/components/tellduslive/cover.py | 3 ++- homeassistant/components/tellduslive/light.py | 3 ++- homeassistant/components/tellduslive/sensor.py | 3 ++- homeassistant/components/tellduslive/switch.py | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index c42f2785576dfd..f348c88daac944 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -53,7 +53,8 @@ } -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up the Daikin HVAC platform. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index 5a005e29989ef3..c4f885f5081d6c 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -13,7 +13,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up the Daikin sensors. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/daikin/switch.py b/homeassistant/components/daikin/switch.py index 29159c60fe9e32..74d0398547808a 100644 --- a/homeassistant/components/daikin/switch.py +++ b/homeassistant/components/daikin/switch.py @@ -10,7 +10,8 @@ ZONE_ICON = 'mdi:home-circle' -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up the platform. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/tellduslive/binary_sensor.py b/homeassistant/components/tellduslive/binary_sensor.py index fc13f75838ab9b..1e258b904631b2 100644 --- a/homeassistant/components/tellduslive/binary_sensor.py +++ b/homeassistant/components/tellduslive/binary_sensor.py @@ -10,7 +10,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up TelldusLive. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/tellduslive/cover.py b/homeassistant/components/tellduslive/cover.py index 6dac00ed7a26ac..b2cb5d9e62ec20 100644 --- a/homeassistant/components/tellduslive/cover.py +++ b/homeassistant/components/tellduslive/cover.py @@ -10,7 +10,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up TelldusLive. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/tellduslive/light.py b/homeassistant/components/tellduslive/light.py index 3847c66b6cbdd2..abbfd8ac92e9cb 100644 --- a/homeassistant/components/tellduslive/light.py +++ b/homeassistant/components/tellduslive/light.py @@ -11,7 +11,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up TelldusLive. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/tellduslive/sensor.py b/homeassistant/components/tellduslive/sensor.py index 156c11c95a7299..8839337590bb4a 100644 --- a/homeassistant/components/tellduslive/sensor.py +++ b/homeassistant/components/tellduslive/sensor.py @@ -42,7 +42,8 @@ } -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up TelldusLive. Can only be called when a user accidentally mentions the platform in their diff --git a/homeassistant/components/tellduslive/switch.py b/homeassistant/components/tellduslive/switch.py index 55275b5b75405b..888feff41f8fa5 100644 --- a/homeassistant/components/tellduslive/switch.py +++ b/homeassistant/components/tellduslive/switch.py @@ -10,7 +10,8 @@ _LOGGER = logging.getLogger(__name__) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): """Old way of setting up TelldusLive. Can only be called when a user accidentally mentions the platform in their From 431cc63aaf6e59024b8ff30d2448be9c048fa3be Mon Sep 17 00:00:00 2001 From: VDRainer <26381449+VDRainer@users.noreply.github.com> Date: Mon, 1 Apr 2019 19:03:18 +0200 Subject: [PATCH 325/605] Trend binary sensor check for state unavailable (#22621) * Trend binary sensor check for state unavailable Fixes: https://github.com/home-assistant/home-assistant/issues/20210 * Fix pylint --- homeassistant/components/trend/binary_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/trend/binary_sensor.py b/homeassistant/components/trend/binary_sensor.py index fe3d10ab72c62b..2b1ae0e083a614 100644 --- a/homeassistant/components/trend/binary_sensor.py +++ b/homeassistant/components/trend/binary_sensor.py @@ -10,7 +10,7 @@ BinarySensorDevice) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, CONF_DEVICE_CLASS, CONF_ENTITY_ID, - CONF_FRIENDLY_NAME, STATE_UNKNOWN, CONF_SENSORS) + CONF_FRIENDLY_NAME, STATE_UNKNOWN, STATE_UNAVAILABLE, CONF_SENSORS) from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import generate_entity_id @@ -140,7 +140,7 @@ def trend_sensor_state_listener(entity, old_state, new_state): state = new_state.attributes.get(self._attribute) else: state = new_state.state - if state != STATE_UNKNOWN: + if state not in (STATE_UNKNOWN, STATE_UNAVAILABLE): sample = (utcnow().timestamp(), float(state)) self.samples.append(sample) self.async_schedule_update_ha_state(True) From bbc4775eab7ed3097bceff73d15b4c6195d590b9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 10:20:13 -0700 Subject: [PATCH 326/605] Disable Z-Wave autoheal (#22628) --- homeassistant/components/zwave/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zwave/const.py b/homeassistant/components/zwave/const.py index fece48655dfd23..67b5341a4e60a2 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -29,7 +29,7 @@ CONF_CONFIG_PATH = 'config_path' CONF_NETWORK_KEY = 'network_key' -DEFAULT_CONF_AUTOHEAL = True +DEFAULT_CONF_AUTOHEAL = False DEFAULT_CONF_USB_STICK_PATH = '/zwaveusbstick' DEFAULT_POLLING_INTERVAL = 60000 DEFAULT_DEBUG = False From 2e02efed104af9fe2e22a78b66961c5d900f68e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 1 Apr 2019 19:33:38 +0200 Subject: [PATCH 327/605] Handle disonnect bug in Tibber library (#22629) --- homeassistant/components/tibber/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index 135437801d934b..19cf6fe65258ea 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.10.0'] +REQUIREMENTS = ['pyTibber==0.10.1'] DOMAIN = 'tibber' diff --git a/requirements_all.txt b/requirements_all.txt index 3868e744228aeb..3c3eabad553821 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -925,7 +925,7 @@ pyRFXtrx==0.23 # pySwitchmate==0.4.5 # homeassistant.components.tibber -pyTibber==0.10.0 +pyTibber==0.10.1 # homeassistant.components.dlink.switch pyW215==0.6.0 From ab2ac60d123fb8c070a03a46e7b8f60a7b676b67 Mon Sep 17 00:00:00 2001 From: Anna Prosvetova Date: Mon, 1 Apr 2019 20:44:46 +0300 Subject: [PATCH 328/605] Fix xiaomi vacuum resume functionality (#22626) --- homeassistant/components/xiaomi_miio/vacuum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/xiaomi_miio/vacuum.py b/homeassistant/components/xiaomi_miio/vacuum.py index c7f69a4033030a..2673a5b897ccab 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -303,7 +303,7 @@ async def _try_command(self, mask_error, func, *args, **kwargs): async def async_start(self): """Start or resume the cleaning task.""" await self._try_command( - "Unable to start the vacuum: %s", self._vacuum.start) + "Unable to start the vacuum: %s", self._vacuum.resume_or_start) async def async_pause(self): """Pause the cleaning task.""" From 1ce622469d878c8d1a527f7562eab455f4ce945a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Marc=20Simard?= Date: Mon, 1 Apr 2019 13:49:53 -0400 Subject: [PATCH 329/605] Fix GTFS variable type mismatch (#22624) --- homeassistant/components/gtfs/sensor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 85f2651d1f6159..c94f97d93dc0f2 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -268,9 +268,8 @@ def get_next_departure(schedule: Any, start_station_id: Any, timetable[idx] = {**row, **extras} # Flag last departures. - for idx in [yesterday_last, today_last]: - if idx is not None: - timetable[idx]['last'] = True + for idx in filter(None, [yesterday_last, today_last]): + timetable[idx]['last'] = True _LOGGER.debug("Timetable: %s", sorted(timetable.keys())) From e78709c5f501592c630186d866268ac01d48f9ea Mon Sep 17 00:00:00 2001 From: etheralm <8655564+etheralm@users.noreply.github.com> Date: Mon, 1 Apr 2019 19:57:11 +0200 Subject: [PATCH 330/605] Add support for Dyson Purecool 2018 Air Purifiers models TP04 and DP04 (#22215) * initial commit initial commit rewrite tests fix merge issue with fan component fix merge issue with fan component * correct line length * change to sync_setup_component for tests * rename services and move services.yaml * move hepa and carbon filter state from sensor to fan * add test for duplicate entities * fix method call tests * fix docstring --- homeassistant/components/dyson/__init__.py | 6 +- homeassistant/components/dyson/climate.py | 15 +- homeassistant/components/dyson/fan.py | 409 ++++++++++++++-- homeassistant/components/dyson/sensor.py | 9 +- homeassistant/components/dyson/services.yaml | 64 +++ homeassistant/components/dyson/vacuum.py | 16 +- homeassistant/components/fan/services.yaml | 10 - requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- script/gen_requirements_all.py | 4 +- tests/components/dyson/test_climate.py | 15 +- tests/components/dyson/test_fan.py | 484 +++++++++++++++++-- tests/components/dyson/test_init.py | 30 +- tests/components/dyson/test_sensor.py | 9 +- tests/components/dyson/test_vacuum.py | 4 +- 15 files changed, 946 insertions(+), 133 deletions(-) create mode 100644 homeassistant/components/dyson/services.yaml diff --git a/homeassistant/components/dyson/__init__.py b/homeassistant/components/dyson/__init__.py index c2e56436bd8b76..eccf8aac364c04 100644 --- a/homeassistant/components/dyson/__init__.py +++ b/homeassistant/components/dyson/__init__.py @@ -3,12 +3,12 @@ import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.const import ( CONF_DEVICES, CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME) from homeassistant.helpers import discovery -import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['libpurecoollink==0.4.2'] +REQUIREMENTS = ['libpurecool==0.5.0'] _LOGGER = logging.getLogger(__name__) @@ -41,7 +41,7 @@ def setup(hass, config): if DYSON_DEVICES not in hass.data: hass.data[DYSON_DEVICES] = [] - from libpurecoollink.dyson import DysonAccount + from libpurecool.dyson import DysonAccount dyson_account = DysonAccount(config[DOMAIN].get(CONF_USERNAME), config[DOMAIN].get(CONF_PASSWORD), config[DOMAIN].get(CONF_LANGUAGE)) diff --git a/homeassistant/components/dyson/climate.py b/homeassistant/components/dyson/climate.py index 3e5c976b1f412e..a24d011623bcb1 100644 --- a/homeassistant/components/dyson/climate.py +++ b/homeassistant/components/dyson/climate.py @@ -11,7 +11,6 @@ STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS - from . import DYSON_DEVICES _LOGGER = logging.getLogger(__name__) @@ -30,7 +29,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - from libpurecoollink.dyson_pure_hotcool_link import DysonPureHotCoolLink + from libpurecool.dyson_pure_hotcool_link import DysonPureHotCoolLink # Get Dyson Devices from parent component. add_devices( [DysonPureHotCoolLinkDevice(device) @@ -54,7 +53,7 @@ async def async_added_to_hass(self): def on_message(self, message): """Call when new messages received from the climate.""" - from libpurecoollink.dyson_pure_state import DysonPureHotCoolState + from libpurecool.dyson_pure_state import DysonPureHotCoolState if isinstance(message, DysonPureHotCoolState): _LOGGER.debug("Message received for climate device %s : %s", @@ -109,7 +108,7 @@ def current_humidity(self): @property def current_operation(self): """Return current operation ie. heat, cool, idle.""" - from libpurecoollink.const import HeatMode, HeatState + from libpurecool.const import HeatMode, HeatState if self._device.state.heat_mode == HeatMode.HEAT_ON.value: if self._device.state.heat_state == HeatState.HEAT_STATE_ON.value: return STATE_HEAT @@ -124,7 +123,7 @@ def operation_list(self): @property def current_fan_mode(self): """Return the fan setting.""" - from libpurecoollink.const import FocusMode + from libpurecool.const import FocusMode if self._device.state.focus_mode == FocusMode.FOCUS_ON.value: return STATE_FOCUS return STATE_DIFFUSE @@ -144,7 +143,7 @@ def set_temperature(self, **kwargs): # Limit the target temperature into acceptable range. target_temp = min(self.max_temp, target_temp) target_temp = max(self.min_temp, target_temp) - from libpurecoollink.const import HeatTarget, HeatMode + from libpurecool.const import HeatTarget, HeatMode self._device.set_configuration( heat_target=HeatTarget.celsius(target_temp), heat_mode=HeatMode.HEAT_ON) @@ -152,7 +151,7 @@ def set_temperature(self, **kwargs): def set_fan_mode(self, fan_mode): """Set new fan mode.""" _LOGGER.debug("Set %s focus mode %s", self.name, fan_mode) - from libpurecoollink.const import FocusMode + from libpurecool.const import FocusMode if fan_mode == STATE_FOCUS: self._device.set_configuration(focus_mode=FocusMode.FOCUS_ON) elif fan_mode == STATE_DIFFUSE: @@ -161,7 +160,7 @@ def set_fan_mode(self, fan_mode): def set_operation_mode(self, operation_mode): """Set operation mode.""" _LOGGER.debug("Set %s heat mode %s", self.name, operation_mode) - from libpurecoollink.const import HeatMode + from libpurecool.const import HeatMode if operation_mode == STATE_HEAT: self._device.set_configuration(heat_mode=HeatMode.HEAT_ON) elif operation_mode == STATE_COOL: diff --git a/homeassistant/components/dyson/fan.py b/homeassistant/components/dyson/fan.py index 743d301df42fe4..0140378968b60b 100644 --- a/homeassistant/components/dyson/fan.py +++ b/homeassistant/components/dyson/fan.py @@ -7,65 +7,150 @@ import voluptuous as vol -from homeassistant.components.fan import ( - DOMAIN, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity) -from homeassistant.const import CONF_ENTITY_ID import homeassistant.helpers.config_validation as cv - +from homeassistant.components.fan import ( + SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity, + SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH) +from homeassistant.const import ATTR_ENTITY_ID from . import DYSON_DEVICES _LOGGER = logging.getLogger(__name__) -CONF_NIGHT_MODE = 'night_mode' - -ATTR_IS_NIGHT_MODE = 'is_night_mode' -ATTR_IS_AUTO_MODE = 'is_auto_mode' +ATTR_NIGHT_MODE = 'night_mode' +ATTR_AUTO_MODE = 'auto_mode' +ATTR_ANGLE_LOW = 'angle_low' +ATTR_ANGLE_HIGH = 'angle_high' +ATTR_FLOW_DIRECTION_FRONT = 'flow_direction_front' +ATTR_TIMER = 'timer' +ATTR_HEPA_FILTER = 'hepa_filter' +ATTR_CARBON_FILTER = 'carbon_filter' +ATTR_DYSON_SPEED = 'dyson_speed' +ATTR_DYSON_SPEED_LIST = 'dyson_speed_list' DEPENDENCIES = ['dyson'] +DYSON_DOMAIN = 'dyson' DYSON_FAN_DEVICES = 'dyson_fan_devices' -SERVICE_SET_NIGHT_MODE = 'dyson_set_night_mode' +SERVICE_SET_NIGHT_MODE = 'set_night_mode' +SERVICE_SET_AUTO_MODE = 'set_auto_mode' +SERVICE_SET_ANGLE = 'set_angle' +SERVICE_SET_FLOW_DIRECTION_FRONT = 'set_flow_direction_front' +SERVICE_SET_TIMER = 'set_timer' +SERVICE_SET_DYSON_SPEED = 'set_speed' DYSON_SET_NIGHT_MODE_SCHEMA = vol.Schema({ - vol.Required(CONF_ENTITY_ID): cv.entity_id, - vol.Required(CONF_NIGHT_MODE): cv.boolean, + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_NIGHT_MODE): cv.boolean, +}) + +SET_AUTO_MODE_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_AUTO_MODE): cv.boolean, +}) + +SET_ANGLE_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_ANGLE_LOW): cv.positive_int, + vol.Required(ATTR_ANGLE_HIGH): cv.positive_int +}) + +SET_FLOW_DIRECTION_FRONT_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_FLOW_DIRECTION_FRONT): cv.boolean +}) + +SET_TIMER_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_TIMER): cv.positive_int +}) + +SET_DYSON_SPEED_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_DYSON_SPEED): cv.positive_int }) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dyson fan components.""" - from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink + from libpurecool.dyson_pure_cool_link import DysonPureCoolLink + from libpurecool.dyson_pure_cool import DysonPureCool + + if discovery_info is None: + return _LOGGER.debug("Creating new Dyson fans") if DYSON_FAN_DEVICES not in hass.data: hass.data[DYSON_FAN_DEVICES] = [] # Get Dyson Devices from parent component - for device in [d for d in hass.data[DYSON_DEVICES] if - isinstance(d, DysonPureCoolLink)]: - dyson_entity = DysonPureCoolLinkDevice(hass, device) - hass.data[DYSON_FAN_DEVICES].append(dyson_entity) + has_purecool_devices = False + device_serials = [device.serial for device in hass.data[DYSON_FAN_DEVICES]] + for device in hass.data[DYSON_DEVICES]: + if device.serial not in device_serials: + if isinstance(device, DysonPureCool): + has_purecool_devices = True + dyson_entity = DysonPureCoolDevice(device) + hass.data[DYSON_FAN_DEVICES].append(dyson_entity) + elif isinstance(device, DysonPureCoolLink): + dyson_entity = DysonPureCoolLinkDevice(hass, device) + hass.data[DYSON_FAN_DEVICES].append(dyson_entity) add_entities(hass.data[DYSON_FAN_DEVICES]) def service_handle(service): """Handle the Dyson services.""" - entity_id = service.data.get(CONF_ENTITY_ID) - night_mode = service.data.get(CONF_NIGHT_MODE) - fan_device = next([fan for fan in hass.data[DYSON_FAN_DEVICES] if - fan.entity_id == entity_id].__iter__(), None) + entity_id = service.data[ATTR_ENTITY_ID] + fan_device = next((fan for fan in hass.data[DYSON_FAN_DEVICES] if + fan.entity_id == entity_id), None) if fan_device is None: _LOGGER.warning("Unable to find Dyson fan device %s", str(entity_id)) return if service.service == SERVICE_SET_NIGHT_MODE: - fan_device.night_mode(night_mode) + fan_device.set_night_mode(service.data[ATTR_NIGHT_MODE]) + + if service.service == SERVICE_SET_AUTO_MODE: + fan_device.set_auto_mode(service.data[ATTR_AUTO_MODE]) + + if service.service == SERVICE_SET_ANGLE: + fan_device.set_angle(service.data[ATTR_ANGLE_LOW], + service.data[ATTR_ANGLE_HIGH]) + + if service.service == SERVICE_SET_FLOW_DIRECTION_FRONT: + fan_device.set_flow_direction_front( + service.data[ATTR_FLOW_DIRECTION_FRONT]) + + if service.service == SERVICE_SET_TIMER: + fan_device.set_timer(service.data[ATTR_TIMER]) + + if service.service == SERVICE_SET_DYSON_SPEED: + fan_device.set_dyson_speed(service.data[ATTR_DYSON_SPEED]) # Register dyson service(s) hass.services.register( - DOMAIN, SERVICE_SET_NIGHT_MODE, service_handle, + DYSON_DOMAIN, SERVICE_SET_NIGHT_MODE, service_handle, schema=DYSON_SET_NIGHT_MODE_SCHEMA) + if has_purecool_devices: + hass.services.register( + DYSON_DOMAIN, SERVICE_SET_AUTO_MODE, service_handle, + schema=SET_AUTO_MODE_SCHEMA) + + hass.services.register( + DYSON_DOMAIN, SERVICE_SET_ANGLE, service_handle, + schema=SET_ANGLE_SCHEMA) + + hass.services.register( + DYSON_DOMAIN, SERVICE_SET_FLOW_DIRECTION_FRONT, service_handle, + schema=SET_FLOW_DIRECTION_FRONT_SCHEMA) + + hass.services.register( + DYSON_DOMAIN, SERVICE_SET_TIMER, service_handle, + schema=SET_TIMER_SCHEMA) + + hass.services.register( + DYSON_DOMAIN, SERVICE_SET_DYSON_SPEED, service_handle, + schema=SET_DYSON_SPEED_SCHEMA) class DysonPureCoolLinkDevice(FanEntity): @@ -84,7 +169,7 @@ async def async_added_to_hass(self): def on_message(self, message): """Call when new messages received from the fan.""" - from libpurecoollink.dyson_pure_state import DysonPureCoolState + from libpurecool.dyson_pure_state import DysonPureCoolState if isinstance(message, DysonPureCoolState): _LOGGER.debug("Message received for fan device %s: %s", self.name, @@ -103,7 +188,7 @@ def name(self): def set_speed(self, speed: str) -> None: """Set the speed of the fan. Never called ??.""" - from libpurecoollink.const import FanSpeed, FanMode + from libpurecool.const import FanSpeed, FanMode _LOGGER.debug("Set fan speed to: %s", speed) @@ -116,7 +201,7 @@ def set_speed(self, speed: str) -> None: def turn_on(self, speed: str = None, **kwargs) -> None: """Turn on the fan.""" - from libpurecoollink.const import FanSpeed, FanMode + from libpurecool.const import FanSpeed, FanMode _LOGGER.debug("Turn on fan %s with speed %s", self.name, speed) if speed: @@ -132,14 +217,14 @@ def turn_on(self, speed: str = None, **kwargs) -> None: def turn_off(self, **kwargs) -> None: """Turn off the fan.""" - from libpurecoollink.const import FanMode + from libpurecool.const import FanMode _LOGGER.debug("Turn off fan %s", self.name) self._device.set_configuration(fan_mode=FanMode.OFF) def oscillate(self, oscillating: bool) -> None: """Turn on/off oscillating.""" - from libpurecoollink.const import Oscillation + from libpurecool.const import Oscillation _LOGGER.debug("Turn oscillation %s for device %s", oscillating, self.name) @@ -166,7 +251,7 @@ def is_on(self): @property def speed(self) -> str: """Return the current speed.""" - from libpurecoollink.const import FanSpeed + from libpurecool.const import FanSpeed if self._device.state: if self._device.state.speed == FanSpeed.FAN_SPEED_AUTO.value: @@ -180,13 +265,13 @@ def current_direction(self): return None @property - def is_night_mode(self): + def night_mode(self): """Return Night mode.""" return self._device.state.night_mode == "ON" - def night_mode(self, night_mode: bool) -> None: + def set_night_mode(self, night_mode: bool) -> None: """Turn fan in night mode.""" - from libpurecoollink.const import NightMode + from libpurecool.const import NightMode _LOGGER.debug("Set %s night mode %s", self.name, night_mode) if night_mode: @@ -195,13 +280,13 @@ def night_mode(self, night_mode: bool) -> None: self._device.set_configuration(night_mode=NightMode.NIGHT_MODE_OFF) @property - def is_auto_mode(self): + def auto_mode(self): """Return auto mode.""" return self._device.state.fan_mode == "AUTO" - def auto_mode(self, auto_mode: bool) -> None: + def set_auto_mode(self, auto_mode: bool) -> None: """Turn fan in auto mode.""" - from libpurecoollink.const import FanMode + from libpurecool.const import FanMode _LOGGER.debug("Set %s auto mode %s", self.name, auto_mode) if auto_mode: @@ -212,7 +297,7 @@ def auto_mode(self, auto_mode: bool) -> None: @property def speed_list(self) -> list: """Get the list of available speeds.""" - from libpurecoollink.const import FanSpeed + from libpurecool.const import FanSpeed supported_speeds = [ FanSpeed.FAN_SPEED_AUTO.value, @@ -239,6 +324,256 @@ def supported_features(self) -> int: def device_state_attributes(self) -> dict: """Return optional state attributes.""" return { - ATTR_IS_NIGHT_MODE: self.is_night_mode, - ATTR_IS_AUTO_MODE: self.is_auto_mode + ATTR_NIGHT_MODE: self.night_mode, + ATTR_AUTO_MODE: self.auto_mode } + + +class DysonPureCoolDevice(FanEntity): + """Representation of a Dyson Purecool (TP04/DP04) fan.""" + + def __init__(self, device): + """Initialize the fan.""" + self._device = device + + async def async_added_to_hass(self): + """Call when entity is added to hass.""" + self.hass.async_add_executor_job( + self._device.add_message_listener, self.on_message) + + def on_message(self, message): + """Call when new messages received from the fan.""" + from libpurecool.dyson_pure_state_v2 import DysonPureCoolV2State + + if isinstance(message, DysonPureCoolV2State): + _LOGGER.debug("Message received for fan device %s: %s", self.name, + message) + self.schedule_update_ha_state() + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def name(self): + """Return the display name of this fan.""" + return self._device.name + + def turn_on(self, speed: str = None, **kwargs) -> None: + """Turn on the fan.""" + _LOGGER.debug("Turn on fan %s", self.name) + + if speed is not None: + self.set_speed(speed) + else: + self._device.turn_on() + + def set_speed(self, speed: str) -> None: + """Set the speed of the fan.""" + from libpurecool.const import FanSpeed + if speed == SPEED_LOW: + self._device.set_fan_speed(FanSpeed.FAN_SPEED_4) + elif speed == SPEED_MEDIUM: + self._device.set_fan_speed(FanSpeed.FAN_SPEED_7) + elif speed == SPEED_HIGH: + self._device.set_fan_speed(FanSpeed.FAN_SPEED_10) + + def turn_off(self, **kwargs): + """Turn off the fan.""" + _LOGGER.debug("Turn off fan %s", self.name) + self._device.turn_off() + + def set_dyson_speed(self, speed: str = None) -> None: + """Set the exact speed of the purecool fan.""" + from libpurecool.const import FanSpeed + + _LOGGER.debug("Set exact speed for fan %s", self.name) + + fan_speed = FanSpeed('{0:04d}'.format(int(speed))) + self._device.set_fan_speed(fan_speed) + + def oscillate(self, oscillating: bool) -> None: + """Turn on/off oscillating.""" + _LOGGER.debug("Turn oscillation %s for device %s", oscillating, + self.name) + + if oscillating: + self._device.enable_oscillation() + else: + self._device.disable_oscillation() + + def set_night_mode(self, night_mode: bool) -> None: + """Turn on/off night mode.""" + _LOGGER.debug("Turn night mode %s for device %s", night_mode, + self.name) + + if night_mode: + self._device.enable_night_mode() + else: + self._device.disable_night_mode() + + def set_auto_mode(self, auto_mode: bool) -> None: + """Turn auto mode on/off.""" + _LOGGER.debug("Turn auto mode %s for device %s", auto_mode, + self.name) + if auto_mode: + self._device.enable_auto_mode() + else: + self._device.disable_auto_mode() + + def set_angle(self, angle_low: int, angle_high: int) -> None: + """Set device angle.""" + _LOGGER.debug("set low %s and high angle %s for device %s", + angle_low, angle_high, self.name) + self._device.enable_oscillation(angle_low, angle_high) + + def set_flow_direction_front(self, + flow_direction_front: bool) -> None: + """Set frontal airflow direction.""" + _LOGGER.debug("Set frontal flow direction to %s for device %s", + flow_direction_front, + self.name) + + if flow_direction_front: + self._device.enable_frontal_direction() + else: + self._device.disable_frontal_direction() + + def set_timer(self, timer) -> None: + """Set timer.""" + _LOGGER.debug("Set timer to %s for device %s", timer, + self.name) + + if timer == 0: + self._device.disable_sleep_timer() + else: + self._device.enable_sleep_timer(timer) + + @property + def oscillating(self): + """Return the oscillation state.""" + return self._device.state and self._device.state.oscillation == "OION" + + @property + def is_on(self): + """Return true if the entity is on.""" + if self._device.state: + return self._device.state.fan_power == "ON" + + @property + def speed(self): + """Return the current speed.""" + from libpurecool.const import FanSpeed + + speed_map = {FanSpeed.FAN_SPEED_1.value: SPEED_LOW, + FanSpeed.FAN_SPEED_2.value: SPEED_LOW, + FanSpeed.FAN_SPEED_3.value: SPEED_LOW, + FanSpeed.FAN_SPEED_4.value: SPEED_LOW, + FanSpeed.FAN_SPEED_AUTO.value: SPEED_MEDIUM, + FanSpeed.FAN_SPEED_5.value: SPEED_MEDIUM, + FanSpeed.FAN_SPEED_6.value: SPEED_MEDIUM, + FanSpeed.FAN_SPEED_7.value: SPEED_MEDIUM, + FanSpeed.FAN_SPEED_8.value: SPEED_HIGH, + FanSpeed.FAN_SPEED_9.value: SPEED_HIGH} + + return speed_map[self._device.state.speed] + + @property + def dyson_speed(self): + """Return the current speed.""" + from libpurecool.const import FanSpeed + + if self._device.state: + if self._device.state.speed == FanSpeed.FAN_SPEED_AUTO.value: + return self._device.state.speed + return int(self._device.state.speed) + + @property + def night_mode(self): + """Return Night mode.""" + return self._device.state.night_mode == "ON" + + @property + def auto_mode(self): + """Return Auto mode.""" + return self._device.state.auto_mode == "ON" + + @property + def angle_low(self): + """Return angle high.""" + return int(self._device.state.oscillation_angle_low) + + @property + def angle_high(self): + """Return angle low.""" + return int(self._device.state.oscillation_angle_high) + + @property + def flow_direction_front(self): + """Return frontal flow direction.""" + return self._device.state.front_direction == 'ON' + + @property + def timer(self): + """Return timer.""" + return self._device.state.sleep_timer + + @property + def hepa_filter(self): + """Return the HEPA filter state.""" + return int(self._device.state.hepa_filter_state) + + @property + def carbon_filter(self): + """Return the carbon filter state.""" + return int(self._device.state.carbon_filter_state) + + @property + def speed_list(self) -> list: + """Get the list of available speeds.""" + return [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] + + @property + def dyson_speed_list(self) -> list: + """Get the list of available dyson speeds.""" + from libpurecool.const import FanSpeed + return [ + int(FanSpeed.FAN_SPEED_1.value), + int(FanSpeed.FAN_SPEED_2.value), + int(FanSpeed.FAN_SPEED_3.value), + int(FanSpeed.FAN_SPEED_4.value), + int(FanSpeed.FAN_SPEED_5.value), + int(FanSpeed.FAN_SPEED_6.value), + int(FanSpeed.FAN_SPEED_7.value), + int(FanSpeed.FAN_SPEED_8.value), + int(FanSpeed.FAN_SPEED_9.value), + int(FanSpeed.FAN_SPEED_10.value), + ] + + @property + def device_serial(self): + """Return fan's serial number.""" + return self._device.serial + + @property + def supported_features(self) -> int: + """Flag supported features.""" + return SUPPORT_OSCILLATE | \ + SUPPORT_SET_SPEED + + @property + def device_state_attributes(self) -> dict: + """Return optional state attributes.""" + return { + ATTR_NIGHT_MODE: self.night_mode, + ATTR_AUTO_MODE: self.auto_mode, + ATTR_ANGLE_LOW: self.angle_low, + ATTR_ANGLE_HIGH: self.angle_high, + ATTR_FLOW_DIRECTION_FRONT: self.flow_direction_front, + ATTR_TIMER: self.timer, + ATTR_HEPA_FILTER: self.hepa_filter, + ATTR_CARBON_FILTER: self.carbon_filter, + ATTR_DYSON_SPEED: self.dyson_speed, + ATTR_DYSON_SPEED_LIST: self.dyson_speed_list + } diff --git a/homeassistant/components/dyson/sensor.py b/homeassistant/components/dyson/sensor.py index ed8987f75c23ec..abf06f15437375 100644 --- a/homeassistant/components/dyson/sensor.py +++ b/homeassistant/components/dyson/sensor.py @@ -37,9 +37,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): devices = [] unit = hass.config.units.temperature_unit # Get Dyson Devices from parent component - from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink - for device in [d for d in hass.data[DYSON_DEVICES] if - isinstance(d, DysonPureCoolLink)]: + from libpurecool.dyson_pure_cool import DysonPureCool + from libpurecool.dyson_pure_cool_link import DysonPureCoolLink + + for device in [d for d in hass.data[DYSON_DEVICES] + if isinstance(d, DysonPureCoolLink) and + not isinstance(d, DysonPureCool)]: devices.append(DysonFilterLifeSensor(device)) devices.append(DysonDustSensor(device)) devices.append(DysonHumiditySensor(device)) diff --git a/homeassistant/components/dyson/services.yaml b/homeassistant/components/dyson/services.yaml new file mode 100644 index 00000000000000..a93b15b4304bf9 --- /dev/null +++ b/homeassistant/components/dyson/services.yaml @@ -0,0 +1,64 @@ +# Describes the format for available fan services + +set_night_mode: + description: Set the fan in night mode. + fields: + entity_id: + description: Name(s) of the entities to enable/disable night mode + example: 'fan.living_room' + night_mode: + description: Night mode status + example: true + +set_auto_mode: + description: Set the fan in auto mode. + fields: + entity_id: + description: Name(s) of the entities to enable/disable auto mode + example: 'fan.living_room' + auto_mode: + description: Auto mode status + example: true + +set_angle: + description: Set the oscillation angle of the selected fan(s). + fields: + entity_id: + description: Name(s) of the entities for which to set the angle + example: 'fan.living_room' + angle_low: + description: The angle at which the oscillation should start + example: 1 + angle_high: + description: The angle at which the oscillation should end + example: 255 + +flow_direction_front: + description: Set the fan flow direction. + fields: + entity_id: + description: Name(s) of the entities to set frontal flow direction for + example: 'fan.living_room' + flow_direction_front: + description: Frontal flow direction + example: true + +set_timer: + description: Set the sleep timer. + fields: + entity_id: + description: Name(s) of the entities to set the sleep timer for + example: 'fan.living_room' + timer: + description: The value in minutes to set the timer to, 0 to disable it + example: 30 + +set_speed: + description: Set the exact speed of the fan. + fields: + entity_id: + description: Name(s) of the entities to set the speed for + example: 'fan.living_room' + timer: + description: Speed + example: 1 \ No newline at end of file diff --git a/homeassistant/components/dyson/vacuum.py b/homeassistant/components/dyson/vacuum.py index 7902cfa1585d28..72c7b95562f850 100644 --- a/homeassistant/components/dyson/vacuum.py +++ b/homeassistant/components/dyson/vacuum.py @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Dyson 360 Eye robot vacuum platform.""" - from libpurecoollink.dyson_360_eye import Dyson360Eye + from libpurecool.dyson_360_eye import Dyson360Eye _LOGGER.debug("Creating new Dyson 360 Eye robot vacuum") if DYSON_360_EYE_DEVICES not in hass.data: @@ -81,7 +81,7 @@ def name(self): @property def status(self): """Return the status of the vacuum cleaner.""" - from libpurecoollink.const import Dyson360EyeMode + from libpurecool.const import Dyson360EyeMode dyson_labels = { Dyson360EyeMode.INACTIVE_CHARGING: "Stopped - Charging", Dyson360EyeMode.INACTIVE_CHARGED: "Stopped - Charged", @@ -106,7 +106,7 @@ def battery_level(self): @property def fan_speed(self): """Return the fan speed of the vacuum cleaner.""" - from libpurecoollink.const import PowerMode + from libpurecool.const import PowerMode speed_labels = { PowerMode.MAX: "Max", PowerMode.QUIET: "Quiet" @@ -128,7 +128,7 @@ def device_state_attributes(self): @property def is_on(self) -> bool: """Return True if entity is on.""" - from libpurecoollink.const import Dyson360EyeMode + from libpurecool.const import Dyson360EyeMode return self._device.state.state in [ Dyson360EyeMode.FULL_CLEAN_INITIATED, @@ -149,7 +149,7 @@ def supported_features(self): @property def battery_icon(self): """Return the battery icon for the vacuum cleaner.""" - from libpurecoollink.const import Dyson360EyeMode + from libpurecool.const import Dyson360EyeMode charging = self._device.state.state in [ Dyson360EyeMode.INACTIVE_CHARGING] @@ -158,7 +158,7 @@ def battery_icon(self): def turn_on(self, **kwargs): """Turn the vacuum on.""" - from libpurecoollink.const import Dyson360EyeMode + from libpurecool.const import Dyson360EyeMode _LOGGER.debug("Turn on device %s", self.name) if self._device.state.state in [Dyson360EyeMode.FULL_CLEAN_PAUSED]: @@ -178,7 +178,7 @@ def stop(self, **kwargs): def set_fan_speed(self, fan_speed, **kwargs): """Set fan speed.""" - from libpurecoollink.const import PowerMode + from libpurecool.const import PowerMode _LOGGER.debug("Set fan speed %s on device %s", fan_speed, self.name) power_modes = { @@ -189,7 +189,7 @@ def set_fan_speed(self, fan_speed, **kwargs): def start_pause(self, **kwargs): """Start, pause or resume the cleaning task.""" - from libpurecoollink.const import Dyson360EyeMode + from libpurecool.const import Dyson360EyeMode if self._device.state.state in [Dyson360EyeMode.FULL_CLEAN_PAUSED]: _LOGGER.debug("Resume device %s", self.name) diff --git a/homeassistant/components/fan/services.yaml b/homeassistant/components/fan/services.yaml index 35a81c7c934bce..16d3742d9ab991 100644 --- a/homeassistant/components/fan/services.yaml +++ b/homeassistant/components/fan/services.yaml @@ -54,16 +54,6 @@ set_direction: description: The direction to rotate. Either 'forward' or 'reverse' example: 'forward' -dyson_set_night_mode: - description: Set the fan in night mode. - fields: - entity_id: - description: Name(s) of the entities to enable/disable night mode - example: 'fan.living_room' - night_mode: - description: Night mode status - example: true - xiaomi_miio_set_buzzer_on: description: Turn the buzzer on. fields: diff --git a/requirements_all.txt b/requirements_all.txt index 3c3eabad553821..ea76277be4d848 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -628,7 +628,7 @@ konnected==0.1.5 lakeside==0.12 # homeassistant.components.dyson -libpurecoollink==0.4.2 +libpurecool==0.5.0 # homeassistant.components.foscam.camera libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bc51c45f84930c..b18ee2b5261a3b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -145,7 +145,7 @@ influxdb==5.2.0 jsonpath==0.75 # homeassistant.components.dyson -libpurecoollink==0.4.2 +libpurecool==0.5.0 # homeassistant.components.soundtouch.media_player libsoundtouch==0.7.2 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 5cc347249f793b..3180f7b82282c3 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 """Generate an updated requirements_all.txt.""" +import fnmatch import importlib import os import pkgutil import re import sys -import fnmatch COMMENT_REQUIREMENTS = ( 'Adafruit-DHT', @@ -74,7 +74,7 @@ 'homematicip', 'influxdb', 'jsonpath', - 'libpurecoollink', + 'libpurecool', 'libsoundtouch', 'luftdaten', 'mbddns', diff --git a/tests/components/dyson/test_climate.py b/tests/components/dyson/test_climate.py index 43ce6344ec4110..778b3bdad4939d 100644 --- a/tests/components/dyson/test_climate.py +++ b/tests/components/dyson/test_climate.py @@ -2,12 +2,13 @@ import unittest from unittest import mock -from libpurecoollink.const import (FocusMode, HeatMode, HeatState, HeatTarget, - TiltState) -from libpurecoollink.dyson_pure_state import DysonPureHotCoolState -from libpurecoollink.dyson_pure_hotcool_link import DysonPureHotCoolLink -from homeassistant.components.dyson import climate as dyson +from libpurecool.const import (FocusMode, HeatMode, + HeatState, HeatTarget, TiltState) +from libpurecool.dyson_pure_hotcool_link import DysonPureHotCoolLink +from libpurecool.dyson_pure_state import DysonPureHotCoolState + from homeassistant.components import dyson as dyson_parent +from homeassistant.components.dyson import climate as dyson from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE from homeassistant.setup import setup_component from tests.common import get_test_home_assistant @@ -110,9 +111,9 @@ def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" self.hass.stop() - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_device_heat_on(), _get_device_cool()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_setup_component_with_parent_discovery(self, mocked_login, mocked_devices): """Test setup_component using discovery.""" diff --git a/tests/components/dyson/test_fan.py b/tests/components/dyson/test_fan.py index a04116f10f2405..0a9469ae80749d 100644 --- a/tests/components/dyson/test_fan.py +++ b/tests/components/dyson/test_fan.py @@ -1,16 +1,28 @@ """Test the Dyson fan component.""" +import json import unittest from unittest import mock -from homeassistant.setup import setup_component +import asynctest +from libpurecool.const import FanSpeed, FanMode, NightMode, Oscillation +from libpurecool.dyson_pure_cool import DysonPureCool +from libpurecool.dyson_pure_cool_link import DysonPureCoolLink +from libpurecool.dyson_pure_state import DysonPureCoolState +from libpurecool.dyson_pure_state_v2 import DysonPureCoolV2State + +import homeassistant.components.dyson.fan as dyson from homeassistant.components import dyson as dyson_parent -from homeassistant.components.dyson import DYSON_DEVICES, fan as dyson -from homeassistant.components.fan import (ATTR_SPEED, ATTR_SPEED_LIST, - ATTR_OSCILLATING) +from homeassistant.components.dyson import DYSON_DEVICES +from homeassistant.components.fan import (DOMAIN, ATTR_SPEED, ATTR_SPEED_LIST, + ATTR_OSCILLATING, SPEED_LOW, + SPEED_MEDIUM, SPEED_HIGH, + SERVICE_OSCILLATE) +from homeassistant.const import (SERVICE_TURN_ON, + SERVICE_TURN_OFF, + ATTR_ENTITY_ID) +from homeassistant.helpers import discovery +from homeassistant.setup import setup_component, async_setup_component from tests.common import get_test_home_assistant -from libpurecoollink.const import FanSpeed, FanMode, NightMode, Oscillation -from libpurecoollink.dyson_pure_state import DysonPureCoolState -from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink class MockDysonState(DysonPureCoolState): @@ -21,6 +33,58 @@ def __init__(self): pass +def _get_dyson_purecool_device(): + """Return a valid device as provided by the Dyson web services.""" + device = mock.Mock(spec=DysonPureCool) + device.serial = "XX-XXXXX-XX" + device.name = "Living room" + device.connect = mock.Mock(return_value=True) + device.auto_connect = mock.Mock(return_value=True) + device.state = mock.Mock() + device.state.oscillation = "OION" + device.state.fan_power = "ON" + device.state.speed = FanSpeed.FAN_SPEED_AUTO.value + device.state.night_mode = "OFF" + device.state.auto_mode = "ON" + device.state.oscillation_angle_low = "0090" + device.state.oscillation_angle_high = "0180" + device.state.front_direction = "ON" + device.state.sleep_timer = 60 + device.state.hepa_filter_state = "0090" + device.state.carbon_filter_state = "0080" + return device + + +def _get_supported_speeds(): + return [ + int(FanSpeed.FAN_SPEED_1.value), + int(FanSpeed.FAN_SPEED_2.value), + int(FanSpeed.FAN_SPEED_3.value), + int(FanSpeed.FAN_SPEED_4.value), + int(FanSpeed.FAN_SPEED_5.value), + int(FanSpeed.FAN_SPEED_6.value), + int(FanSpeed.FAN_SPEED_7.value), + int(FanSpeed.FAN_SPEED_8.value), + int(FanSpeed.FAN_SPEED_9.value), + int(FanSpeed.FAN_SPEED_10.value), + ] + + +def _get_config(): + """Return a config dictionary.""" + return {dyson_parent.DOMAIN: { + dyson_parent.CONF_USERNAME: "email", + dyson_parent.CONF_PASSWORD: "password", + dyson_parent.CONF_LANGUAGE: "GB", + dyson_parent.CONF_DEVICES: [ + { + "device_id": "XX-XXXXX-XX", + "device_ip": "192.168.0.1" + } + ] + }} + + def _get_device_with_no_state(): """Return a device with no state.""" device = mock.Mock() @@ -64,8 +128,8 @@ def _get_device_on(): return device -class DysonTest(unittest.TestCase): - """Dyson Sensor component test class.""" +class DysonSetupTest(unittest.TestCase): + """Dyson component setup tests.""" def setUp(self): # pylint: disable=invalid-name """Set up things to be run when tests are started.""" @@ -79,24 +143,39 @@ def test_setup_component_with_no_devices(self): """Test setup component with no devices.""" self.hass.data[dyson.DYSON_DEVICES] = [] add_entities = mock.MagicMock() - dyson.setup_platform(self.hass, None, add_entities) + dyson.setup_platform(self.hass, None, add_entities, mock.Mock()) add_entities.assert_called_with([]) def test_setup_component(self): """Test setup component with devices.""" def _add_device(devices): - assert len(devices) == 1 + assert len(devices) == 2 assert devices[0].name == "Device_name" device_fan = _get_device_on() + device_purecool_fan = _get_dyson_purecool_device() device_non_fan = _get_device_off() - self.hass.data[dyson.DYSON_DEVICES] = [device_fan, device_non_fan] + self.hass.data[dyson.DYSON_DEVICES] = [device_fan, + device_purecool_fan, + device_non_fan] dyson.setup_platform(self.hass, None, _add_device) - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + +class DysonTest(unittest.TestCase): + """Dyson fan component test class.""" + + def setUp(self): # pylint: disable=invalid-name + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def tearDown(self): # pylint: disable=invalid-name + """Stop everything that was started.""" + self.hass.stop() + + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_device_on()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_get_state_attributes(self, mocked_login, mocked_devices): """Test async added to hass.""" setup_component(self.hass, dyson_parent.DOMAIN, { @@ -108,18 +187,18 @@ def test_get_state_attributes(self, mocked_login, mocked_devices): }) self.hass.block_till_done() state = self.hass.states.get("{}.{}".format( - dyson.DOMAIN, + DOMAIN, mocked_devices.return_value[0].name)) - assert dyson.ATTR_IS_NIGHT_MODE in state.attributes - assert dyson.ATTR_IS_AUTO_MODE in state.attributes + assert dyson.ATTR_NIGHT_MODE in state.attributes + assert dyson.ATTR_AUTO_MODE in state.attributes assert ATTR_SPEED in state.attributes assert ATTR_SPEED_LIST in state.attributes assert ATTR_OSCILLATING in state.attributes - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_device_on()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_async_added_to_hass(self, mocked_login, mocked_devices): """Test async added to hass.""" setup_component(self.hass, dyson_parent.DOMAIN, { @@ -161,11 +240,11 @@ def test_dyson_turn_night_mode(self): device = _get_device_on() component = dyson.DysonPureCoolLinkDevice(self.hass, device) assert not component.should_poll - component.night_mode(True) + component.set_night_mode(True) set_config = device.set_configuration set_config.assert_called_with(night_mode=NightMode.NIGHT_MODE_ON) - component.night_mode(False) + component.set_night_mode(False) set_config = device.set_configuration set_config.assert_called_with(night_mode=NightMode.NIGHT_MODE_OFF) @@ -173,22 +252,22 @@ def test_is_night_mode(self): """Test night mode.""" device = _get_device_on() component = dyson.DysonPureCoolLinkDevice(self.hass, device) - assert not component.is_night_mode + assert not component.night_mode device = _get_device_off() component = dyson.DysonPureCoolLinkDevice(self.hass, device) - assert component.is_night_mode + assert component.night_mode def test_dyson_turn_auto_mode(self): """Test turn on/off fan with auto mode.""" device = _get_device_on() component = dyson.DysonPureCoolLinkDevice(self.hass, device) assert not component.should_poll - component.auto_mode(True) + component.set_auto_mode(True) set_config = device.set_configuration set_config.assert_called_with(fan_mode=FanMode.AUTO) - component.auto_mode(False) + component.set_auto_mode(False) set_config = device.set_configuration set_config.assert_called_with(fan_mode=FanMode.FAN) @@ -196,11 +275,11 @@ def test_is_auto_mode(self): """Test auto mode.""" device = _get_device_on() component = dyson.DysonPureCoolLinkDevice(self.hass, device) - assert not component.is_auto_mode + assert not component.auto_mode device = _get_device_auto() component = dyson.DysonPureCoolLinkDevice(self.hass, device) - assert component.is_auto_mode + assert component.auto_mode def test_dyson_turn_on_speed(self): """Test turn on fan with specified speed.""" @@ -320,14 +399,355 @@ def test_service_set_night_mode(self): self.hass.data[DYSON_DEVICES] = [] dyson_device.entity_id = 'fan.living_room' self.hass.data[dyson.DYSON_FAN_DEVICES] = [dyson_device] - dyson.setup_platform(self.hass, None, mock.MagicMock()) + dyson.setup_platform(self.hass, None, + mock.MagicMock(), mock.MagicMock()) - self.hass.services.call(dyson.DOMAIN, dyson.SERVICE_SET_NIGHT_MODE, + self.hass.services.call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_NIGHT_MODE, {"entity_id": "fan.bed_room", "night_mode": True}, True) - assert not dyson_device.night_mode.called + assert dyson_device.set_night_mode.call_count == 0 - self.hass.services.call(dyson.DOMAIN, dyson.SERVICE_SET_NIGHT_MODE, + self.hass.services.call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_NIGHT_MODE, {"entity_id": "fan.living_room", "night_mode": True}, True) - dyson_device.night_mode.assert_called_with(True) + dyson_device.set_night_mode.assert_called_with(True) + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_turn_on(devices, login, hass): + """Test turn on.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.bed_room"}, True) + assert device.turn_on.call_count == 0 + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.living_room"}, True) + assert device.turn_on.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_speed(devices, login, hass): + """Test set speed.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.bed_room", + ATTR_SPEED: SPEED_LOW}, True) + assert device.set_fan_speed.call_count == 0 + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.living_room", + ATTR_SPEED: SPEED_LOW}, True) + device.set_fan_speed.assert_called_with(FanSpeed.FAN_SPEED_4) + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.living_room", + ATTR_SPEED: SPEED_MEDIUM}, True) + device.set_fan_speed.assert_called_with(FanSpeed.FAN_SPEED_7) + + await hass.services.async_call(DOMAIN, SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "fan.living_room", + ATTR_SPEED: SPEED_HIGH}, True) + device.set_fan_speed.assert_called_with(FanSpeed.FAN_SPEED_10) + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_turn_off(devices, login, hass): + """Test turn off.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "fan.bed_room"}, True) + assert device.turn_off.call_count == 0 + + await hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "fan.living_room"}, True) + assert device.turn_off.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_dyson_speed(devices, login, hass): + """Test set exact dyson speed.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_DYSON_SPEED, + {ATTR_ENTITY_ID: "fan.bed_room", + dyson.ATTR_DYSON_SPEED: + int(FanSpeed.FAN_SPEED_2.value)}, + True) + assert device.set_fan_speed.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_DYSON_SPEED, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_DYSON_SPEED: + int(FanSpeed.FAN_SPEED_2.value)}, + True) + device.set_fan_speed.assert_called_with(FanSpeed.FAN_SPEED_2) + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_oscillate(devices, login, hass): + """Test set oscillation.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_OSCILLATE, + {ATTR_ENTITY_ID: "fan.bed_room", + ATTR_OSCILLATING: True}, True) + assert device.enable_oscillation.call_count == 0 + + await hass.services.async_call(DOMAIN, SERVICE_OSCILLATE, + {ATTR_ENTITY_ID: "fan.living_room", + ATTR_OSCILLATING: True}, True) + assert device.enable_oscillation.call_count == 1 + + await hass.services.async_call(DOMAIN, SERVICE_OSCILLATE, + {ATTR_ENTITY_ID: "fan.living_room", + ATTR_OSCILLATING: False}, True) + assert device.disable_oscillation.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_night_mode(devices, login, hass): + """Test set night mode.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_NIGHT_MODE, + {"entity_id": "fan.bed_room", + "night_mode": True}, True) + assert device.enable_night_mode.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_NIGHT_MODE, + {"entity_id": "fan.living_room", + "night_mode": True}, True) + assert device.enable_night_mode.call_count == 1 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_NIGHT_MODE, + {"entity_id": "fan.living_room", + "night_mode": False}, True) + assert device.disable_night_mode.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_auto_mode(devices, login, hass): + """Test set auto mode.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_AUTO_MODE, + {ATTR_ENTITY_ID: "fan.bed_room", + dyson.ATTR_AUTO_MODE: True}, True) + assert device.enable_auto_mode.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_AUTO_MODE, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_AUTO_MODE: True}, True) + assert device.enable_auto_mode.call_count == 1 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_AUTO_MODE, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_AUTO_MODE: False}, True) + assert device.disable_auto_mode.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_angle(devices, login, hass): + """Test set angle.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, dyson.SERVICE_SET_ANGLE, + {ATTR_ENTITY_ID: "fan.bed_room", + dyson.ATTR_ANGLE_LOW: 90, + dyson.ATTR_ANGLE_HIGH: 180}, True) + assert device.enable_oscillation.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, dyson.SERVICE_SET_ANGLE, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_ANGLE_LOW: 90, + dyson.ATTR_ANGLE_HIGH: 180}, True) + device.enable_oscillation.assert_called_with(90, 180) + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_flow_direction_front(devices, login, hass): + """Test set frontal flow direction.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_FLOW_DIRECTION_FRONT, + {ATTR_ENTITY_ID: "fan.bed_room", + dyson.ATTR_FLOW_DIRECTION_FRONT: True}, + True) + assert device.enable_frontal_direction.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_FLOW_DIRECTION_FRONT, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_FLOW_DIRECTION_FRONT: True}, + True) + assert device.enable_frontal_direction.call_count == 1 + + await hass.services.async_call(dyson.DYSON_DOMAIN, + dyson.SERVICE_SET_FLOW_DIRECTION_FRONT, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_FLOW_DIRECTION_FRONT: False}, + True) + assert device.disable_frontal_direction.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_set_timer(devices, login, hass): + """Test set timer.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + + await hass.services.async_call(dyson.DYSON_DOMAIN, dyson.SERVICE_SET_TIMER, + {ATTR_ENTITY_ID: "fan.bed_room", + dyson.ATTR_TIMER: 60}, + True) + assert device.enable_frontal_direction.call_count == 0 + + await hass.services.async_call(dyson.DYSON_DOMAIN, dyson.SERVICE_SET_TIMER, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_TIMER: 60}, + True) + device.enable_sleep_timer.assert_called_with(60) + + await hass.services.async_call(dyson.DYSON_DOMAIN, dyson.SERVICE_SET_TIMER, + {ATTR_ENTITY_ID: "fan.living_room", + dyson.ATTR_TIMER: 0}, + True) + assert device.disable_sleep_timer.call_count == 1 + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_attributes(devices, login, hass): + """Test state attributes.""" + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + fan_state = hass.states.get("fan.living_room") + attributes = fan_state.attributes + + assert fan_state.state == "on" + assert attributes[dyson.ATTR_NIGHT_MODE] is False + assert attributes[dyson.ATTR_AUTO_MODE] is True + assert attributes[dyson.ATTR_ANGLE_LOW] == 90 + assert attributes[dyson.ATTR_ANGLE_HIGH] == 180 + assert attributes[dyson.ATTR_FLOW_DIRECTION_FRONT] is True + assert attributes[dyson.ATTR_TIMER] == 60 + assert attributes[dyson.ATTR_HEPA_FILTER] == 90 + assert attributes[dyson.ATTR_CARBON_FILTER] == 80 + assert attributes[dyson.ATTR_DYSON_SPEED] == FanSpeed.FAN_SPEED_AUTO.value + assert attributes[ATTR_SPEED] == SPEED_MEDIUM + assert attributes[ATTR_OSCILLATING] is True + assert attributes[dyson.ATTR_DYSON_SPEED_LIST] == _get_supported_speeds() + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_update_state(devices, login, hass): + """Test state update.""" + device = devices.return_value[0] + await async_setup_component(hass, dyson.DYSON_DOMAIN, _get_config()) + await hass.async_block_till_done() + event = {"msg": "CURRENT-STATE", + "product-state": {"fpwr": "OFF", "fdir": "OFF", "auto": "OFF", + "oscs": "ON", "oson": "ON", "nmod": "OFF", + "rhtm": "ON", "fnst": "FAN", "ercd": "11E1", + "wacd": "NONE", "nmdv": "0004", "fnsp": "0002", + "bril": "0002", "corf": "ON", "cflr": "0085", + "hflr": "0095", "sltm": "OFF", "osal": "0045", + "osau": "0095", "ancp": "CUST"}} + device.state = DysonPureCoolV2State(json.dumps(event)) + + callback = device.add_message_listener.call_args_list[0][0][0] + callback(device.state) + await hass.async_block_till_done() + fan_state = hass.states.get("fan.living_room") + attributes = fan_state.attributes + + assert fan_state.state == "off" + assert attributes[dyson.ATTR_NIGHT_MODE] is False + assert attributes[dyson.ATTR_AUTO_MODE] is False + assert attributes[dyson.ATTR_ANGLE_LOW] == 45 + assert attributes[dyson.ATTR_ANGLE_HIGH] == 95 + assert attributes[dyson.ATTR_FLOW_DIRECTION_FRONT] is False + assert attributes[dyson.ATTR_TIMER] == "OFF" + assert attributes[dyson.ATTR_HEPA_FILTER] == 95 + assert attributes[dyson.ATTR_CARBON_FILTER] == 85 + assert attributes[dyson.ATTR_DYSON_SPEED] == \ + int(FanSpeed.FAN_SPEED_2.value) + assert attributes[ATTR_SPEED] is SPEED_LOW + assert attributes[ATTR_OSCILLATING] is False + assert attributes[dyson.ATTR_DYSON_SPEED_LIST] == _get_supported_speeds() + + +@asynctest.patch('libpurecool.dyson.DysonAccount.login', return_value=True) +@asynctest.patch('libpurecool.dyson.DysonAccount.devices', + return_value=[_get_dyson_purecool_device()]) +async def test_purecool_component_setup_only_once(devices, login, hass): + """Test if entities are created only once.""" + config = _get_config() + await async_setup_component(hass, dyson_parent.DOMAIN, config) + await hass.async_block_till_done() + discovery.load_platform(hass, "fan", dyson_parent.DOMAIN, {}, config) + await hass.async_block_till_done() + + fans = [fan for fan in hass.data[DOMAIN].entities + if fan.platform.platform_name == dyson_parent.DOMAIN] + + assert len(fans) == 1 + assert fans[0].device_serial == "XX-XXXXX-XX" diff --git a/tests/components/dyson/test_init.py b/tests/components/dyson/test_init.py index 2e7b05b06cd69a..cc8c04a1559adb 100644 --- a/tests/components/dyson/test_init.py +++ b/tests/components/dyson/test_init.py @@ -43,7 +43,7 @@ def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" self.hass.stop() - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=False) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=False) def test_dyson_login_failed(self, mocked_login): """Test if Dyson connection failed.""" dyson.setup(self.hass, {dyson.DOMAIN: { @@ -53,8 +53,8 @@ def test_dyson_login_failed(self, mocked_login): }}) assert mocked_login.call_count == 1 - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', return_value=[]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[]) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_login(self, mocked_login, mocked_devices): """Test valid connection to dyson web service.""" dyson.setup(self.hass, {dyson.DOMAIN: { @@ -67,9 +67,9 @@ def test_dyson_login(self, mocked_login, mocked_devices): assert len(self.hass.data[dyson.DYSON_DEVICES]) == 0 @mock.patch('homeassistant.helpers.discovery.load_platform') - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_available()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_custom_conf(self, mocked_login, mocked_devices, mocked_discovery): """Test device connection using custom configuration.""" @@ -89,9 +89,9 @@ def test_dyson_custom_conf(self, mocked_login, mocked_devices, assert len(self.hass.data[dyson.DYSON_DEVICES]) == 1 assert mocked_discovery.call_count == 4 - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_not_available()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_custom_conf_device_not_available(self, mocked_login, mocked_devices): """Test device connection with an invalid device.""" @@ -110,9 +110,9 @@ def test_dyson_custom_conf_device_not_available(self, mocked_login, assert mocked_devices.call_count == 1 assert len(self.hass.data[dyson.DYSON_DEVICES]) == 0 - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_error()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_custom_conf_device_error(self, mocked_login, mocked_devices): """Test device connection with device raising an exception.""" @@ -132,9 +132,9 @@ def test_dyson_custom_conf_device_error(self, mocked_login, assert len(self.hass.data[dyson.DYSON_DEVICES]) == 0 @mock.patch('homeassistant.helpers.discovery.load_platform') - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_available()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_custom_conf_with_unknown_device(self, mocked_login, mocked_devices, mocked_discovery): @@ -156,9 +156,9 @@ def test_dyson_custom_conf_with_unknown_device(self, mocked_login, assert mocked_discovery.call_count == 0 @mock.patch('homeassistant.helpers.discovery.load_platform') - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_available()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_discovery(self, mocked_login, mocked_devices, mocked_discovery): """Test device connection using discovery.""" @@ -174,9 +174,9 @@ def test_dyson_discovery(self, mocked_login, mocked_devices, assert len(self.hass.data[dyson.DYSON_DEVICES]) == 1 assert mocked_discovery.call_count == 4 - @mock.patch('libpurecoollink.dyson.DysonAccount.devices', + @mock.patch('libpurecool.dyson.DysonAccount.devices', return_value=[_get_dyson_account_device_not_available()]) - @mock.patch('libpurecoollink.dyson.DysonAccount.login', return_value=True) + @mock.patch('libpurecool.dyson.DysonAccount.login', return_value=True) def test_dyson_discovery_device_not_available(self, mocked_login, mocked_devices): """Test device connection with discovery and invalid device.""" diff --git a/tests/components/dyson/test_sensor.py b/tests/components/dyson/test_sensor.py index 3218038c7e3dc7..67c34d4d180445 100644 --- a/tests/components/dyson/test_sensor.py +++ b/tests/components/dyson/test_sensor.py @@ -2,16 +2,17 @@ import unittest from unittest import mock +from libpurecool.dyson_pure_cool_link import DysonPureCoolLink + +from homeassistant.components.dyson import sensor as dyson from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, \ STATE_OFF -from homeassistant.components.dyson import sensor as dyson from tests.common import get_test_home_assistant -from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink def _get_device_without_state(): """Return a valid device provide by Dyson web services.""" - device = mock.Mock(spec=DysonPureCoolLink) + device = mock.Mock() device.name = "Device_name" device.state = None device.environmental_state = None @@ -20,7 +21,7 @@ def _get_device_without_state(): def _get_with_state(): """Return a valid device with state values.""" - device = mock.Mock() + device = mock.Mock(spec=DysonPureCoolLink) device.name = "Device_name" device.state = mock.Mock() device.state.filter_life = 100 diff --git a/tests/components/dyson/test_vacuum.py b/tests/components/dyson/test_vacuum.py index 05ad8cf0db7e20..cdf76c975aed50 100644 --- a/tests/components/dyson/test_vacuum.py +++ b/tests/components/dyson/test_vacuum.py @@ -2,8 +2,8 @@ import unittest from unittest import mock -from libpurecoollink.dyson_360_eye import Dyson360Eye -from libpurecoollink.const import Dyson360EyeMode, PowerMode +from libpurecool.const import Dyson360EyeMode, PowerMode +from libpurecool.dyson_360_eye import Dyson360Eye from homeassistant.components.dyson import vacuum as dyson from homeassistant.components.dyson.vacuum import Dyson360EyeDevice From 82296aeb7127fa3532098ea7b53e2f33b54e7c42 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Mon, 1 Apr 2019 17:36:29 -0500 Subject: [PATCH 331/605] Amcrest: Add on/off support & attributes. Bump amcrest to 1.3.0 (#22418) * Amcrest: Add on/off support & attributes to camera entity. Bump amcrest package to 1.3.0. Add support for turn_on & turn_off services. Add implementation of is_recording method, as well as brand, model, hardware_version, machine_name, serial_number, software_build and software_version attributes. Bump amcrest package to 1.3.0 required for above changes and also handles errors in storage commands which resolves #19982. * Update per review Rebase to upstream/dev. Remove video_enabled property and setter and replace with _enable_video_stream method. Remove static attributes from camera and sensors. --- homeassistant/components/amcrest/__init__.py | 2 +- homeassistant/components/amcrest/camera.py | 87 +++++++++++++++++++- homeassistant/components/amcrest/sensor.py | 13 --- requirements_all.txt | 2 +- 4 files changed, 85 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index 84dc0b5bb014e0..295b798c3b1967 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -12,7 +12,7 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['amcrest==1.2.7'] +REQUIREMENTS = ['amcrest==1.3.0'] DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 63c2c720781a2b..853d5404dab326 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -3,7 +3,7 @@ import logging from homeassistant.components.camera import ( - Camera, SUPPORT_STREAM) + Camera, SUPPORT_ON_OFF, SUPPORT_STREAM) from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import CONF_NAME from homeassistant.helpers.aiohttp_client import ( @@ -39,18 +39,23 @@ def __init__(self, hass, amcrest): super(AmcrestCam, self).__init__() self._name = amcrest.name self._camera = amcrest.device - self._base_url = self._camera.get_base_url() self._ffmpeg = hass.data[DATA_FFMPEG] self._ffmpeg_arguments = amcrest.ffmpeg_arguments self._stream_source = amcrest.stream_source self._resolution = amcrest.resolution self._token = self._auth = amcrest.authentication + self._is_recording = False + self._model = None self._snapshot_lock = asyncio.Lock() async def async_camera_image(self): """Return a still image response from the camera.""" from amcrest import AmcrestError + if not self.is_on: + _LOGGER.error( + 'Attempt to take snaphot when %s camera is off', self.name) + return None async with self._snapshot_lock: try: # Send the request to snap a picture and return raw jpg data @@ -59,7 +64,8 @@ async def async_camera_image(self): return response.data except AmcrestError as error: _LOGGER.error( - 'Could not get camera image due to error %s', error) + 'Could not get image from %s camera due to error: %s', + self.name, error) return None async def handle_async_mjpeg_stream(self, request): @@ -94,6 +100,8 @@ async def handle_async_mjpeg_stream(self, request): finally: await stream.close() + # Entity property overrides + @property def name(self): """Return the name of this camera.""" @@ -102,9 +110,80 @@ def name(self): @property def supported_features(self): """Return supported features.""" - return SUPPORT_STREAM + return SUPPORT_ON_OFF | SUPPORT_STREAM + + # Camera property overrides + + @property + def is_recording(self): + """Return true if the device is recording.""" + return self._is_recording + + @property + def brand(self): + """Return the camera brand.""" + return 'Amcrest' + + @property + def model(self): + """Return the camera model.""" + return self._model @property def stream_source(self): """Return the source of the stream.""" return self._camera.rtsp_url(typeno=self._resolution) + + @property + def is_on(self): + """Return true if on.""" + return self.is_streaming + + # Other Entity method overrides + + def update(self): + """Update entity status.""" + from amcrest import AmcrestError + + _LOGGER.debug('Pulling data from %s camera', self.name) + if self._model is None: + try: + self._model = self._camera.device_type.split('=')[-1].strip() + except AmcrestError as error: + _LOGGER.error( + 'Could not get %s camera model due to error: %s', + self.name, error) + self._model = '' + try: + self.is_streaming = self._camera.video_enabled + self._is_recording = self._camera.record_mode == 'Manual' + except AmcrestError as error: + _LOGGER.error( + 'Could not get %s camera attributes due to error: %s', + self.name, error) + + # Other Camera method overrides + + def turn_off(self): + """Turn off camera.""" + self._enable_video_stream(False) + + def turn_on(self): + """Turn on camera.""" + self._enable_video_stream(True) + + # Utility methods + + def _enable_video_stream(self, enable): + """Enable or disable camera video stream.""" + from amcrest import AmcrestError + + try: + self._camera.video_enabled = enable + except AmcrestError as error: + _LOGGER.error( + 'Could not %s %s camera video stream due to error: %s', + 'enable' if enable else 'disable', self.name, error) + else: + self.is_streaming = enable + self.schedule_update_ha_state() diff --git a/homeassistant/components/amcrest/sensor.py b/homeassistant/components/amcrest/sensor.py index c721914c73cf81..68bc86da94c444 100644 --- a/homeassistant/components/amcrest/sensor.py +++ b/homeassistant/components/amcrest/sensor.py @@ -75,19 +75,6 @@ def update(self): """Get the latest data and updates the state.""" _LOGGER.debug("Pulling data from %s sensor.", self._name) - try: - version, build_date = self._camera.software_information - self._attrs['Build Date'] = build_date.split('=')[-1] - self._attrs['Version'] = version.split('=')[-1] - except ValueError: - self._attrs['Build Date'] = 'Not Available' - self._attrs['Version'] = 'Not Available' - - try: - self._attrs['Serial Number'] = self._camera.serial_number - except ValueError: - self._attrs['Serial Number'] = 'Not Available' - if self._sensor_type == 'motion_detector': self._state = self._camera.is_motion_detected self._attrs['Record Mode'] = self._camera.record_mode diff --git a/requirements_all.txt b/requirements_all.txt index ea76277be4d848..4a67141d7437be 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -155,7 +155,7 @@ alarmdecoder==1.13.2 alpha_vantage==2.1.0 # homeassistant.components.amcrest -amcrest==1.2.7 +amcrest==1.3.0 # homeassistant.components.androidtv.media_player androidtv==0.0.14 From a7e613616c5d771f2c5fb9a39345fdfc7c48161c Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Tue, 2 Apr 2019 10:27:58 +1100 Subject: [PATCH 332/605] change library to georss_generic_client (#22615) --- homeassistant/components/geo_rss_events/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- script/gen_requirements_all.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/geo_rss_events/sensor.py b/homeassistant/components/geo_rss_events/sensor.py index ab406f9241e750..f71a60c2e83e89 100644 --- a/homeassistant/components/geo_rss_events/sensor.py +++ b/homeassistant/components/geo_rss_events/sensor.py @@ -20,7 +20,7 @@ CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS, CONF_URL) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['georss_client==0.5'] +REQUIREMENTS = ['georss_generic_client==0.2'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 4a67141d7437be..9f7ee3d3f3a6ce 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -473,7 +473,7 @@ geizhals==0.0.9 geojson_client==0.3 # homeassistant.components.geo_rss_events.sensor -georss_client==0.5 +georss_generic_client==0.2 # homeassistant.components.gitter.sensor gitterpy==0.1.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b18ee2b5261a3b..76ec85148e2ace 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -108,7 +108,7 @@ gTTS-token==1.1.3 geojson_client==0.3 # homeassistant.components.geo_rss_events.sensor -georss_client==0.5 +georss_generic_client==0.2 # homeassistant.components.ffmpeg ha-ffmpeg==2.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 3180f7b82282c3..33a7b4fd16fee1 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -59,7 +59,7 @@ 'feedparser-homeassistant', 'foobot_async', 'geojson_client', - 'georss_client', + 'georss_generic_client', 'gTTS-token', 'ha-ffmpeg', 'hangups', From e70803266923327f813ecc4bd142a9044171e1c1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 Apr 2019 02:41:08 +0200 Subject: [PATCH 333/605] Support GET params for websocket ingress path (#22638) --- homeassistant/components/hassio/ingress.py | 5 +++++ tests/components/hassio/test_ingress.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 6c1ef389712dff..04241f53de9c0b 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -71,9 +71,14 @@ async def _handle_websocket( ws_server = web.WebSocketResponse() await ws_server.prepare(request) + # Preparing url = self._create_url(addon, path) source_header = _init_header(request, addon) + # Support GET query + if request.query_string: + url = "{}?{}".format(url, request.query_string) + # Start proxy async with self._websession.ws_connect( url, headers=source_header diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 4e071ba24fd897..4b72eda4c2596a 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -136,7 +136,8 @@ async def test_ingress_request_delete( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ws"), ("core", "ws.php"), - ("local", "panel/config/stream"), ("jk_921", "hulk") + ("local", "panel/config/stream"), ("jk_921", "hulk"), + ("demo", "ws/connection?id=9&token=SJAKWS283") ]) async def test_ingress_websocket( hassio_client, build_type, aioclient_mock): From 1e26151069c7cefe1401d08ffc3e0a20271d7745 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 17:42:04 -0700 Subject: [PATCH 334/605] Require static-check success first for rest of workflow (#22635) * Require static-check success first * Update config.yml --- .circleci/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b4f22601bb5c8a..9c9d75d934bd54 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -213,18 +213,26 @@ workflows: build: jobs: - static-check - - pre-install-all-requirements + - pre-install-all-requirements: + requires: + - static-check - pylint: requires: - pre-install-all-requirements - pre-test: name: pre-test 3.5.5 + requires: + - static-check python: 3.5.5-stretch - pre-test: name: pre-test 3.6 + requires: + - static-check python: 3.6-stretch - pre-test: name: pre-test 3.7 + requires: + - static-check python: 3.7-stretch - test: name: test 3.5.5 From 39eaa7fc8d71018fa5ec81b856ace7086e9260e1 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 17:43:29 -0700 Subject: [PATCH 335/605] Add trusted networks deprecating warning (#22487) * Add trusted networks deprecating warning * Update auth.py * Update auth.py * Update auth.py * Update auth.py * Tweak --- homeassistant/components/http/auth.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 4736ef123913ae..7b8508894ce73d 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,6 +190,12 @@ async def auth_middleware(request, handler): elif (trusted_networks and await async_validate_trusted_networks(request)): + _LOGGER.warning( + 'Access from trusted networks without auth token is going to ' + 'be removed in Home Assistant 0.96. Configure the trusted ' + 'networks auth provider or use long-lived access tokens to ' + 'access %s from %s', + request.path, request[KEY_REAL_IP]) authenticated = True elif (support_legacy and HTTP_HEADER_HA_AUTH in request.headers and From 7646dc00e0a5d9f7f2ff7b3c5a8c62d1139c70a1 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 20:31:05 -0700 Subject: [PATCH 336/605] Add codecov (#22649) --- .circleci/config.yml | 5 +++-- requirements_test.txt | 1 + requirements_test_all.txt | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c9d75d934bd54..cde04d08e4049c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -172,13 +172,14 @@ jobs: - install - run: - name: run tests + name: run tests with code coverage command: | . venv/bin/activate + CC_SWITCH="--cov --cov-report=" TESTFILES=$(circleci tests glob "tests/**/test_*.py" | circleci tests split --split-by=timings) - if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty + codecov - store_test_results: path: test-reports diff --git a/requirements_test.txt b/requirements_test.txt index bf96353144c1aa..0cd8c583f38ec4 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -2,6 +2,7 @@ # make new things fail. Manually update these pins when pulling in a # new version asynctest==0.12.2 +codecov==2.0.15 coveralls==1.2.0 flake8-docstrings==1.3.0 flake8==3.7.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 76ec85148e2ace..974cfef9cd03d1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -3,6 +3,7 @@ # make new things fail. Manually update these pins when pulling in a # new version asynctest==0.12.2 +codecov==2.0.15 coveralls==1.2.0 flake8-docstrings==1.3.0 flake8==3.7.7 From 2578c8525bf59108edd81a1528f10e44edf19e5b Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Tue, 2 Apr 2019 05:57:25 +0200 Subject: [PATCH 337/605] Qwikswitch fix listen loop (#22600) * Qwikswitch fix listen loop * 0.93 fix qwikcord upstream --- homeassistant/components/qwikswitch/__init__.py | 5 +++-- .../components/qwikswitch/binary_sensor.py | 2 +- homeassistant/components/qwikswitch/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/qwikswitch/test_init.py | 17 ++++++++++------- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/qwikswitch/__init__.py b/homeassistant/components/qwikswitch/__init__.py index 63e30a9491edec..23144ed82b8304 100644 --- a/homeassistant/components/qwikswitch/__init__.py +++ b/homeassistant/components/qwikswitch/__init__.py @@ -19,7 +19,7 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyqwikswitch==0.8'] +REQUIREMENTS = ['pyqwikswitch==0.93'] _LOGGER = logging.getLogger(__name__) @@ -119,7 +119,8 @@ async def async_turn_off(self, **_): async def async_setup(hass, config): """Qwiskswitch component setup.""" from pyqwikswitch.async_ import QSUsb - from pyqwikswitch import CMD_BUTTONS, QS_CMD, QS_ID, QSType, SENSORS + from pyqwikswitch.qwikswitch import ( + CMD_BUTTONS, QS_CMD, QS_ID, QSType, SENSORS) # Add cmd's to in /&listen packets will fire events # By default only buttons of type [TOGGLE,SCENE EXE,LEVEL] diff --git a/homeassistant/components/qwikswitch/binary_sensor.py b/homeassistant/components/qwikswitch/binary_sensor.py index 17021f7a9e9bc3..6cdc29deae4d66 100644 --- a/homeassistant/components/qwikswitch/binary_sensor.py +++ b/homeassistant/components/qwikswitch/binary_sensor.py @@ -35,7 +35,7 @@ class QSBinarySensor(QSEntity, BinarySensorDevice): def __init__(self, sensor): """Initialize the sensor.""" - from pyqwikswitch import SENSORS + from pyqwikswitch.qwikswitch import SENSORS super().__init__(sensor['id'], sensor['name']) self.channel = sensor['channel'] diff --git a/homeassistant/components/qwikswitch/sensor.py b/homeassistant/components/qwikswitch/sensor.py index 07d0247e4f60b5..b9ccb3c3a7b524 100644 --- a/homeassistant/components/qwikswitch/sensor.py +++ b/homeassistant/components/qwikswitch/sensor.py @@ -33,7 +33,7 @@ class QSSensor(QSEntity): def __init__(self, sensor): """Initialize the sensor.""" - from pyqwikswitch import SENSORS + from pyqwikswitch.qwikswitch import SENSORS super().__init__(sensor['id'], sensor['name']) self.channel = sensor['channel'] diff --git a/requirements_all.txt b/requirements_all.txt index 9f7ee3d3f3a6ce..1ef9af9c970059 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1238,7 +1238,7 @@ pypollencom==2.2.3 pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch -pyqwikswitch==0.8 +pyqwikswitch==0.93 # homeassistant.components.nmbs.sensor pyrail==0.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 974cfef9cd03d1..5d58e42e8b288b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -234,7 +234,7 @@ pyotp==2.2.6 pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch -pyqwikswitch==0.8 +pyqwikswitch==0.93 # homeassistant.components.smartthings pysmartapp==0.3.2 diff --git a/tests/components/qwikswitch/test_init.py b/tests/components/qwikswitch/test_init.py index 76655f32816b9b..d6ad0607d42f94 100644 --- a/tests/components/qwikswitch/test_init.py +++ b/tests/components/qwikswitch/test_init.py @@ -7,6 +7,7 @@ from homeassistant.components.qwikswitch import DOMAIN as QWIKSWITCH from homeassistant.bootstrap import async_setup_component from tests.test_util.aiohttp import mock_aiohttp_client +from aiohttp.client_exceptions import ClientError _LOGGER = logging.getLogger(__name__) @@ -23,6 +24,8 @@ def decode(self, _): try: res = list.pop(self, 0) _LOGGER.debug("MockResponseList popped %s: %s", res, self) + if isinstance(res, Exception): + raise res return res except IndexError: raise AssertionError("MockResponseList empty") @@ -54,7 +57,7 @@ def aioclient_mock(): yield mock_session -async def test_binary_sensor_device(hass, aioclient_mock): +async def test_binary_sensor_device(hass, aioclient_mock): # noqa """Test a binary sensor device.""" config = { 'qwikswitch': { @@ -75,7 +78,8 @@ async def test_binary_sensor_device(hass, aioclient_mock): hass.bus.async_fire(EVENT_HOMEASSISTANT_START) LISTEN.append('{"id":"@a00001","cmd":"","data":"4e0e1601","rssi":"61%"}') - LISTEN.append('') # Will cause a sleep + LISTEN.append(ClientError()) # Will cause a sleep + await hass.async_block_till_done() state_obj = hass.states.get('binary_sensor.s1') assert state_obj.state == 'on' @@ -87,7 +91,7 @@ async def test_binary_sensor_device(hass, aioclient_mock): assert state_obj.state == 'off' -async def test_sensor_device(hass, aioclient_mock): +async def test_sensor_device(hass, aioclient_mock): # noqa """Test a sensor device.""" config = { 'qwikswitch': { @@ -100,8 +104,8 @@ async def test_sensor_device(hass, aioclient_mock): } } await async_setup_component(hass, QWIKSWITCH, config) - await hass.async_block_till_done() + await hass.async_block_till_done() state_obj = hass.states.get('sensor.ss1') assert state_obj.state == 'None' @@ -110,8 +114,7 @@ async def test_sensor_device(hass, aioclient_mock): LISTEN.append( '{"id":"@a00001","name":"ss1","type":"rel",' '"val":"4733800001a00000"}') - LISTEN.append('') # Will cause a sleep - await LISTEN.wait_till_empty(hass) # await hass.async_block_till_done() + await hass.async_block_till_done() state_obj = hass.states.get('sensor.ss1') - assert state_obj.state == 'None' + assert state_obj.state == '416' From 48189dd15221bc1c088808750754f56d345e934f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 21:51:43 -0700 Subject: [PATCH 338/605] Run PyLint under Python 3.5 (#22642) * Run PyLint under Python 3.5 * Remove -q from pip install to debug * Upgrade setuptools before install * Use correct cache key for pylint --- .circleci/config.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cde04d08e4049c..cfc968a1a6a55e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: tag: type: string - default: latest + default: latest docker: - image: circleci/python:<< parameters.tag >> - image: circleci/buildpack-deps:stretch @@ -53,6 +53,7 @@ commands: python3 -m venv venv . venv/bin/activate pip install -q -U pip + pip install -q -U setuptools <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> @@ -107,27 +108,27 @@ jobs: pre-install-all-requirements: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch steps: - checkout - docker-prereqs - install-requirements: - python: 3.7-stretch + python: 3.5.5-stretch all: true test: true pylint: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch parallelism: 2 steps: - checkout - docker-prereqs - install-requirements: - python: 3.7-stretch + python: 3.5.5-stretch all: true test: true - install From 16e0953f267f6b24cbbfebee282a48cb542f7bb4 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 2 Apr 2019 08:57:58 +0100 Subject: [PATCH 339/605] Fix racy homekit_controller platform setup caused by #22368 (#22655) --- homeassistant/components/homekit_controller/__init__.py | 3 +-- homeassistant/components/homekit_controller/connection.py | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 44af8bffe26a74..2a43d0ac9ce5c4 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -191,8 +191,7 @@ def discovery_dispatch(service, discovery_info): return _LOGGER.debug('Discovered unique device %s', hkid) - device = HKDevice(hass, host, port, model, hkid, config_num, config) - hass.data[KNOWN_DEVICES][hkid] = device + HKDevice(hass, host, port, model, hkid, config_num, config) hass.data[KNOWN_DEVICES] = {} discovery.listen(hass, SERVICE_HOMEKIT, discovery_dispatch) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index d875b91eb2c54d..2ca568b547fd3e 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -7,7 +7,8 @@ from homeassistant.helpers.event import call_later from .const import ( - CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, PAIRING_FILE, HOMEKIT_DIR + CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_DEVICES, + PAIRING_FILE, HOMEKIT_DIR ) @@ -76,6 +77,8 @@ def __init__(self, hass, host, port, model, hkid, config_num, config): self.pairing = self.controller.pairings.get(hkid) + hass.data[KNOWN_DEVICES][hkid] = self + if self.pairing is not None: self.accessory_setup() else: From 3bd37d6a657ff676c919515784cdb631c138e754 Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Tue, 2 Apr 2019 14:11:26 +0100 Subject: [PATCH 340/605] Improve evohome exception handling and fix bugs (#22140) * Use latest client library, evohomeclient v0.3.1 * Fix issue #22097: Failed to call service climate/turn_on... * BUGFIX: handle case where a Zone doesn't have a temperature * BUGFIX: missing exception handler, and inappropriate delint hints * Improve exception handling, and also better messages * improve code (REDACT secrets); remove TODOs * minor refactor - improve error message * more refactoring - improve error message * remove TODOs * update to latest evohomeclient library * Use latest client library, evohomeclient v0.3.1 * Fix issue #22097: Failed to call service climate/turn_on... * BUGFIX: handle case where a Zone doesn't have a temperature * BUGFIX: missing exception handler, and inappropriate delint hints * Improve exception handling, and also better messages * improve code (REDACT secrets); remove TODOs * minor refactor - improve error message * more refactoring - improve error message * remove TODOs * update to latest evohomeclient library * fix requests for houndci-bot * Tidy up requests exception handling * Correct lint error * update to latest client library * minor de-lint * more cleanup of exceptions, messages * refactored for new exception * fix error in requirements*_all.txt * de-lint * delint unused import * import 3rd-party library only inside methods * change honeywell tests * delint, fix typo * we dont log usernames, passwords, etc. * de-lint --- homeassistant/components/evohome/__init__.py | 81 +++++++++--------- homeassistant/components/evohome/climate.py | 85 +++++++++++++------ homeassistant/components/honeywell/climate.py | 8 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/honeywell/test_climate.py | 5 +- 6 files changed, 106 insertions(+), 77 deletions(-) diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 52bb77516e671c..87a563ecd6d07f 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -8,20 +8,18 @@ from datetime import timedelta import logging -from requests.exceptions import HTTPError +import requests.exceptions import voluptuous as vol from homeassistant.const import ( CONF_SCAN_INTERVAL, CONF_USERNAME, CONF_PASSWORD, - EVENT_HOMEASSISTANT_START, - HTTP_BAD_REQUEST, HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS -) + EVENT_HOMEASSISTANT_START) from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['evohomeclient==0.2.8'] +REQUIREMENTS = ['evohomeclient==0.3.2'] _LOGGER = logging.getLogger(__name__) @@ -43,6 +41,10 @@ }), }, extra=vol.ALLOW_EXTRA) +CONF_SECRETS = [ + CONF_USERNAME, CONF_PASSWORD, +] + # These are used to help prevent E501 (line too long) violations. GWS = 'gateways' TCS = 'temperatureControlSystems' @@ -66,51 +68,40 @@ def setup(hass, hass_config): scan_interval = timedelta( minutes=(scan_interval.total_seconds() + 59) // 60) - from evohomeclient2 import EvohomeClient + import evohomeclient2 try: - client = EvohomeClient( + client = evo_data['client'] = evohomeclient2.EvohomeClient( evo_data['params'][CONF_USERNAME], evo_data['params'][CONF_PASSWORD], debug=False ) - except HTTPError as err: - if err.response.status_code == HTTP_BAD_REQUEST: - _LOGGER.error( - "setup(): Failed to connect with the vendor's web servers. " - "Check your username (%s), and password are correct." - "Unable to continue. Resolve any errors and restart HA.", - evo_data['params'][CONF_USERNAME] - ) - - elif err.response.status_code == HTTP_SERVICE_UNAVAILABLE: - _LOGGER.error( - "setup(): Failed to connect with the vendor's web servers. " - "The server is not contactable. Unable to continue. " - "Resolve any errors and restart HA." - ) - - elif err.response.status_code == HTTP_TOO_MANY_REQUESTS: - _LOGGER.error( - "setup(): Failed to connect with the vendor's web servers. " - "You have exceeded the api rate limit. Unable to continue. " - "Wait a while (say 10 minutes) and restart HA." - ) - - else: - raise # We don't expect/handle any other HTTPErrors + except evohomeclient2.AuthenticationError as err: + _LOGGER.error( + "setup(): Failed to authenticate with the vendor's server. " + "Check your username and password are correct. " + "Resolve any errors and restart HA. Message is: %s", + err + ) + return False + except requests.exceptions.ConnectionError: + _LOGGER.error( + "setup(): Unable to connect with the vendor's server. " + "Check your network and the vendor's status page. " + "Resolve any errors and restart HA." + ) return False - finally: # Redact username, password as no longer needed - evo_data['params'][CONF_USERNAME] = 'REDACTED' - evo_data['params'][CONF_PASSWORD] = 'REDACTED' + finally: # Redact any config data that's no longer needed + for parameter in CONF_SECRETS: + evo_data['params'][parameter] = 'REDACTED' \ + if evo_data['params'][parameter] else None - evo_data['client'] = client evo_data['status'] = {} - # Redact any installation data we'll never need + # Redact any installation data that's no longer needed for loc in client.installation_info: loc['locationInfo']['locationId'] = 'REDACTED' loc['locationInfo']['locationOwner'] = 'REDACTED' @@ -120,18 +111,21 @@ def setup(hass, hass_config): # Pull down the installation configuration loc_idx = evo_data['params'][CONF_LOCATION_IDX] - try: evo_data['config'] = client.installation_info[loc_idx] + except IndexError: - _LOGGER.warning( - "setup(): Parameter '%s'=%s, is outside its range (0-%s)", - CONF_LOCATION_IDX, loc_idx, len(client.installation_info) - 1) + _LOGGER.error( + "setup(): config error, '%s' = %s, but its valid range is 0-%s. " + "Unable to continue. Fix any configuration errors and restart HA.", + CONF_LOCATION_IDX, loc_idx, len(client.installation_info) - 1 + ) return False if _LOGGER.isEnabledFor(logging.DEBUG): tmp_loc = dict(evo_data['config']) tmp_loc['locationInfo']['postcode'] = 'REDACTED' + if 'dhw' in tmp_loc[GWS][0][TCS][0]: # if this location has DHW... tmp_loc[GWS][0][TCS][0]['dhw'] = '...' @@ -139,6 +133,11 @@ def setup(hass, hass_config): load_platform(hass, 'climate', DOMAIN, {}, hass_config) + if 'dhw' in evo_data['config'][GWS][0][TCS][0]: + _LOGGER.warning( + "setup(): DHW found, but this component doesn't support DHW." + ) + @callback def _first_update(event): """When HA has started, the hub knows to retrieve it's first update.""" diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index eea34e070012a4..cf6c21df10f96b 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -2,22 +2,22 @@ from datetime import datetime, timedelta import logging -from requests.exceptions import HTTPError +import requests.exceptions from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_AWAY_MODE, SUPPORT_ON_OFF, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.const import ( - CONF_SCAN_INTERVAL, HTTP_TOO_MANY_REQUESTS, PRECISION_HALVES, STATE_OFF, - TEMP_CELSIUS) + CONF_SCAN_INTERVAL, HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS, + PRECISION_HALVES, STATE_OFF, TEMP_CELSIUS) from homeassistant.core import callback from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, dispatcher_send) from . import ( CONF_LOCATION_IDX, DATA_EVOHOME, DISPATCHER_EVOHOME, EVO_CHILD, EVO_PARENT, - GWS, SCAN_INTERVAL_DEFAULT, TCS) + GWS, TCS) _LOGGER = logging.getLogger(__name__) @@ -81,7 +81,7 @@ async def async_setup_platform(hass, hass_config, async_add_entities, # evohomeclient has exposed no means of accessing non-default location # (i.e. loc_idx > 0) other than using a protected member, such as below - tcs_obj_ref = client.locations[loc_idx]._gateways[0]._control_systems[0] # noqa E501; pylint: disable=protected-access + tcs_obj_ref = client.locations[loc_idx]._gateways[0]._control_systems[0] # noqa: E501; pylint: disable=protected-access _LOGGER.debug( "Found Controller, id=%s [%s], name=%s (location_idx=%s)", @@ -128,23 +128,43 @@ def _connect(self, packet): if packet['to'] & self._type and packet['signal'] == 'refresh': self.async_schedule_update_ha_state(force_refresh=True) - def _handle_requests_exceptions(self, err): - if err.response.status_code == HTTP_TOO_MANY_REQUESTS: - # execute a backoff: pause, and also reduce rate - old_interval = self._params[CONF_SCAN_INTERVAL] - new_interval = min(old_interval, SCAN_INTERVAL_DEFAULT) * 2 - self._params[CONF_SCAN_INTERVAL] = new_interval + def _handle_exception(self, err): + try: + import evohomeclient2 + raise err + + except evohomeclient2.AuthenticationError: + _LOGGER.error( + "Failed to (re)authenticate with the vendor's server. " + "This may be a temporary error. Message is: %s", + err + ) + except requests.exceptions.ConnectionError: + # this appears to be common with Honeywell's servers _LOGGER.warning( - "API rate limit has been exceeded. Suspending polling for %s " - "seconds, and increasing '%s' from %s to %s seconds", - new_interval * 3, CONF_SCAN_INTERVAL, old_interval, - new_interval) + "Unable to connect with the vendor's server. " + "Check your network and the vendor's status page." + ) - self._timers['statusUpdated'] = datetime.now() + new_interval * 3 + except requests.exceptions.HTTPError: + if err.response.status_code == HTTP_SERVICE_UNAVAILABLE: + _LOGGER.warning( + "Vendor says their server is currently unavailable. " + "This may be temporary; check the vendor's status page." + ) + + elif err.response.status_code == HTTP_TOO_MANY_REQUESTS: + _LOGGER.warning( + "The vendor's API rate limit has been exceeded. " + "So will cease polling, and will resume after %s seconds.", + (self._params[CONF_SCAN_INTERVAL] * 3).total_seconds() + ) + self._timers['statusUpdated'] = datetime.now() + \ + self._params[CONF_SCAN_INTERVAL] * 3 - else: - raise err # we dont handle any other HTTPErrors + else: + raise # we don't expect/handle any other HTTPErrors @property def name(self) -> str: @@ -239,7 +259,8 @@ def target_temperature(self): @property def current_temperature(self): """Return the current temperature of the evohome Zone.""" - return self._status['temperatureStatus']['temperature'] + return (self._status['temperatureStatus']['temperature'] + if self._status['temperatureStatus']['isAvailable'] else None) @property def current_operation(self): @@ -284,9 +305,11 @@ def _set_temperature(self, temperature, until=None): - None for PermanentOverride (i.e. indefinitely) """ try: + import evohomeclient2 self._obj.set_temperature(temperature, until) - except HTTPError as err: - self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member + except (requests.exceptions.RequestException, + evohomeclient2.AuthenticationError) as err: + self._handle_exception(err) def set_temperature(self, **kwargs): """Set new target temperature, indefinitely.""" @@ -334,9 +357,11 @@ def set_operation_mode(self, operation_mode): def _set_operation_mode(self, operation_mode): if operation_mode == EVO_FOLLOW: try: - self._obj.cancel_temp_override(self._obj) - except HTTPError as err: - self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member + import evohomeclient2 + self._obj.cancel_temp_override() + except (requests.exceptions.RequestException, + evohomeclient2.AuthenticationError) as err: + self._handle_exception(err) elif operation_mode == EVO_TEMPOVER: _LOGGER.error( @@ -496,9 +521,11 @@ def turn_away_mode_off(self): def _set_operation_mode(self, operation_mode): try: + import evohomeclient2 self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access - except HTTPError as err: - self._handle_requests_exceptions(err) + except (requests.exceptions.RequestException, + evohomeclient2.AuthenticationError) as err: + self._handle_exception(err) def set_operation_mode(self, operation_mode): """Set new target operation mode for the TCS. @@ -532,10 +559,12 @@ def update(self): loc_idx = self._params[CONF_LOCATION_IDX] try: + import evohomeclient2 self._status.update( self._client.locations[loc_idx].status()[GWS][0][TCS][0]) - except HTTPError as err: # check if we've exceeded the api rate limit - self._handle_requests_exceptions(err) + except (requests.exceptions.RequestException, + evohomeclient2.AuthenticationError) as err: + self._handle_exception(err) else: self._timers['statusUpdated'] = datetime.now() self._available = True diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index a76f992a76af0d..55a7fb5aa4859e 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -5,7 +5,6 @@ https://home-assistant.io/components/climate.honeywell/ """ import logging -import socket import datetime import requests @@ -21,7 +20,7 @@ CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE, CONF_REGION) -REQUIREMENTS = ['evohomeclient==0.2.8', 'somecomfort==0.5.2'] +REQUIREMENTS = ['evohomeclient==0.3.2', 'somecomfort==0.5.2'] _LOGGER = logging.getLogger(__name__) @@ -78,9 +77,10 @@ def _setup_round(username, password, config, add_entities): [RoundThermostat(evo_api, zone['id'], i == 0, away_temp)], True ) - except socket.error: + except requests.exceptions.RequestException as err: _LOGGER.error( - "Connection error logging into the honeywell evohome web service") + "Connection error logging into the honeywell evohome web service, " + "hint: %s", err) return False return True diff --git a/requirements_all.txt b/requirements_all.txt index 1ef9af9c970059..8c85c9edbc5358 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -414,7 +414,7 @@ eternalegypt==0.0.6 # homeassistant.components.evohome # homeassistant.components.honeywell.climate -evohomeclient==0.2.8 +evohomeclient==0.3.2 # homeassistant.components.dlib_face_detect.image_processing # homeassistant.components.dlib_face_identify.image_processing diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5d58e42e8b288b..e08255c246d7fd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -92,7 +92,7 @@ ephem==3.7.6.0 # homeassistant.components.evohome # homeassistant.components.honeywell.climate -evohomeclient==0.2.8 +evohomeclient==0.3.2 # homeassistant.components.feedreader feedparser-homeassistant==5.2.2.dev1 diff --git a/tests/components/honeywell/test_climate.py b/tests/components/honeywell/test_climate.py index e8e0c0a2929ce3..2674dac6b1ee53 100644 --- a/tests/components/honeywell/test_climate.py +++ b/tests/components/honeywell/test_climate.py @@ -1,9 +1,9 @@ """The test the Honeywell thermostat module.""" -import socket import unittest from unittest import mock import voluptuous as vol +import requests.exceptions import somecomfort from homeassistant.const import ( @@ -247,7 +247,8 @@ def test_eu_setup_error(self, mock_round, mock_evo): honeywell.CONF_AWAY_TEMPERATURE: 20, honeywell.CONF_REGION: 'eu', } - mock_evo.return_value.temperatures.side_effect = socket.error + mock_evo.return_value.temperatures.side_effect = \ + requests.exceptions.RequestException add_entities = mock.MagicMock() hass = mock.MagicMock() assert not honeywell.setup_platform(hass, config, add_entities) From 04271549633b612834ee560b123ab9279280e3ea Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Tue, 2 Apr 2019 11:28:55 -0400 Subject: [PATCH 341/605] Don't force updates on ZHA Electrical Measurement sensor. (#22647) --- homeassistant/components/zha/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 56ce97c87a066b..d45d8f8c30ddc3 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -86,7 +86,7 @@ def pressure_formatter(value): } FORCE_UPDATE_REGISTRY = { - ELECTRICAL_MEASUREMENT: True + ELECTRICAL_MEASUREMENT: False } From b8b3f4e88fc66a916664f515d616b19ded019bc4 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Tue, 2 Apr 2019 18:31:29 +0200 Subject: [PATCH 342/605] Fix pytest durations parameter (#22658) * Fix durations parameter * Update config.yml --- .circleci/config.yml | 2 +- tox.ini | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cfc968a1a6a55e..b1fdc2be93b142 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -178,7 +178,7 @@ jobs: . venv/bin/activate CC_SWITCH="--cov --cov-report=" TESTFILES=$(circleci tests glob "tests/**/test_*.py" | circleci tests split --split-by=timings) - pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} + pytest --timeout=9 --durations=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty codecov diff --git a/tox.ini b/tox.ini index b8995d9e877b94..d0c4336f544314 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ skip_missing_interpreters = True [testenv] basepython = {env:PYTHON3_PATH:python3} commands = - pytest --timeout=9 --duration=10 -qq -o console_output_style=count -p no:sugar {posargs} + pytest --timeout=9 --durations=10 -qq -o console_output_style=count -p no:sugar {posargs} {toxinidir}/script/check_dirty deps = -r{toxinidir}/requirements_test_all.txt @@ -13,7 +13,7 @@ deps = [testenv:cov] commands = - pytest --timeout=9 --duration=10 -qq -o console_output_style=count -p no:sugar --cov --cov-report= {posargs} + pytest --timeout=9 --durations=10 -qq -o console_output_style=count -p no:sugar --cov --cov-report= {posargs} {toxinidir}/script/check_dirty deps = -r{toxinidir}/requirements_test_all.txt From e00ae35e07b571d6cdc1c69cc68f81798792e89e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 2 Apr 2019 09:34:11 -0700 Subject: [PATCH 343/605] Admin service to automatically add empty schema (#22637) * Admin service to automatically add empty schema * Lint --- homeassistant/components/cloud/__init__.py | 5 ++--- homeassistant/helpers/service.py | 7 ++++--- tests/helpers/test_service.py | 21 +++++++++++++++++++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index fca5b292033dca..41045ba1f91265 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -187,11 +187,10 @@ async def _service_handler(service): await cloud.remote.disconnect() await prefs.async_update(remote_enabled=False) - empty_schema = vol.Schema({}) hass.helpers.service.async_register_admin_service( - DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler, empty_schema) + DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler) hass.helpers.service.async_register_admin_service( - DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler, empty_schema) + DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler) await http_api.async_setup(hass) hass.async_create_task(hass.helpers.discovery.async_load_platform( diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index f8af3bdb1c57c3..3892dbb660733c 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -333,9 +333,10 @@ async def _handle_service_platform_call(func, data, entities, context): @bind_hass @ha.callback -def async_register_admin_service(hass: typing.HomeAssistantType, domain: str, - service: str, service_func: Callable, - schema: vol.Schema) -> None: +def async_register_admin_service( + hass: typing.HomeAssistantType, domain: str, + service: str, service_func: Callable, + schema: vol.Schema = vol.Schema({}, extra=vol.PREVENT_EXTRA)) -> None: """Register a service that requires admin access.""" @wraps(service_func) async def admin_handler(call): diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index a36785b6ba0a19..f59a01ec268469 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -406,7 +406,11 @@ async def mock_service(call): calls.append(call) hass.helpers.service.async_register_admin_service( - 'test', 'test', mock_service, vol.Schema({}) + 'test', 'test', mock_service + ) + hass.helpers.service.async_register_admin_service( + 'test', 'test2', mock_service, + vol.Schema({vol.Required('required'): cv.boolean}) ) with pytest.raises(exceptions.UnknownUser): @@ -423,8 +427,21 @@ async def mock_service(call): )) assert len(calls) == 0 + with pytest.raises(vol.Invalid): + await hass.services.async_call( + 'test', 'test', {'invalid': True}, blocking=True, + context=ha.Context(user_id=hass_admin_user.id)) + assert len(calls) == 0 + + with pytest.raises(vol.Invalid): + await hass.services.async_call( + 'test', 'test2', {}, blocking=True, context=ha.Context( + user_id=hass_admin_user.id + )) + assert len(calls) == 0 + await hass.services.async_call( - 'test', 'test', {}, blocking=True, context=ha.Context( + 'test', 'test2', {'required': True}, blocking=True, context=ha.Context( user_id=hass_admin_user.id )) assert len(calls) == 1 From d6e28621151aa6e71046a252a4b708197eb2fa94 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 2 Apr 2019 09:51:44 -0700 Subject: [PATCH 344/605] Ignore code coverages for component without test (#22653) --- .coveragerc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.coveragerc b/.coveragerc index 145efe5c847a4b..cae3bf423a95d4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -21,6 +21,7 @@ omit = homeassistant/components/alarmdecoder/* homeassistant/components/alarmdotcom/alarm_control_panel.py homeassistant/components/alpha_vantage/sensor.py + homeassistant/components/amazon_polly/tts.py homeassistant/components/ambient_station/* homeassistant/components/amcrest/* homeassistant/components/ampio/* @@ -49,6 +50,7 @@ omit = homeassistant/components/aws_lambda/notify.py homeassistant/components/aws_sns/notify.py homeassistant/components/aws_sqs/notify.py + homeassistant/components/baidu/tts.py homeassistant/components/bbb_gpio/* homeassistant/components/bbox/device_tracker.py homeassistant/components/bbox/sensor.py @@ -93,6 +95,7 @@ omit = homeassistant/components/clicksend_tts/notify.py homeassistant/components/cloudflare/* homeassistant/components/cmus/media_player.py + homeassistant/components/co2signal/* homeassistant/components/coinbase/* homeassistant/components/comed_hourly_pricing/sensor.py homeassistant/components/comfoconnect/* @@ -342,6 +345,7 @@ omit = homeassistant/components/meteo_france/* homeassistant/components/metoffice/sensor.py homeassistant/components/metoffice/weather.py + homeassistant/components/microsoft/tts.py homeassistant/components/miflora/sensor.py homeassistant/components/mikrotik/device_tracker.py homeassistant/components/mill/climate.py @@ -423,6 +427,7 @@ omit = homeassistant/components/pencom/switch.py homeassistant/components/philips_js/media_player.py homeassistant/components/pi_hole/sensor.py + homeassistant/components/picotts/tts.py homeassistant/components/piglow/light.py homeassistant/components/pilight/* homeassistant/components/ping/binary_sensor.py @@ -606,10 +611,6 @@ omit = homeassistant/components/trafikverket_weatherstation/sensor.py homeassistant/components/transmission/* homeassistant/components/travisci/sensor.py - homeassistant/components/tts/amazon_polly.py - homeassistant/components/tts/baidu.py - homeassistant/components/tts/microsoft.py - homeassistant/components/tts/picotts.py homeassistant/components/tuya/* homeassistant/components/twilio_call/notify.py homeassistant/components/twilio_sms/notify.py @@ -651,6 +652,7 @@ omit = homeassistant/components/wirelesstag/* homeassistant/components/worldtidesinfo/sensor.py homeassistant/components/worxlandroid/sensor.py + homeassistant/components/wunderlist/* homeassistant/components/x10/light.py homeassistant/components/xbox_live/sensor.py homeassistant/components/xeoma/camera.py @@ -664,7 +666,7 @@ omit = homeassistant/components/yale_smart_alarm/alarm_control_panel.py homeassistant/components/yamaha/media_player.py homeassistant/components/yamaha_musiccast/media_player.py - homeassistant/components/yeelight/light.py + homeassistant/components/yeelight/* homeassistant/components/yeelightsunflower/light.py homeassistant/components/yi/camera.py homeassistant/components/zabbix/* From 429e2cdde8fe7209e2fe54aef6b50b94ec66ef17 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Tue, 2 Apr 2019 12:59:38 -0400 Subject: [PATCH 345/605] Return 0 for failed Foscam streams (#22651) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models * changing logic for success and return none --- homeassistant/components/foscam/camera.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index a11d2f48f625b9..b6f2162d57a9d9 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -58,7 +58,10 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._media_port = self._foscam_session.get_port_info()[1]['mediaPort'] + self._media_port = None + result, response = self._foscam_session.get_port_info() + if result == 0: + self._media_port = response['mediaPort'] def camera_image(self): """Return a still image response from the camera.""" @@ -73,16 +76,20 @@ def camera_image(self): @property def supported_features(self): """Return supported features.""" - return SUPPORT_STREAM + if self._media_port: + return SUPPORT_STREAM + return 0 @property def stream_source(self): """Return the stream source.""" - return 'rtsp://{}:{}@{}:{}/videoMain'.format( - self._username, - self._password, - self._foscam_session.host, - self._media_port) + if self._media_port: + return 'rtsp://{}:{}@{}:{}/videoMain'.format( + self._username, + self._password, + self._foscam_session.host, + self._media_port) + return None @property def motion_detection_enabled(self): From 6c14e7afa7ff12dfadd673ef6f337d9ee56770fe Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Tue, 2 Apr 2019 19:29:48 +0200 Subject: [PATCH 346/605] Add battery sensor to Homematic IP (#22630) --- .../homematicip_cloud/binary_sensor.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 786a28a70a5fd3..071b4a0a3fbe8b 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -29,8 +29,8 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the HomematicIP Cloud binary sensor from a config entry.""" from homematicip.aio.device import ( - AsyncShutterContact, AsyncMotionDetectorIndoor, AsyncSmokeDetector, - AsyncWaterSensor, AsyncRotaryHandleSensor, + AsyncDevice, AsyncShutterContact, AsyncMotionDetectorIndoor, + AsyncSmokeDetector, AsyncWaterSensor, AsyncRotaryHandleSensor, AsyncMotionDetectorPushButton, AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro) @@ -56,6 +56,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): AsyncWeatherSensorPro)): devices.append(HomematicipStormSensor(home, device)) devices.append(HomematicipSunshineSensor(home, device)) + if isinstance(device, AsyncDevice) and device.lowBat is not None: + devices.append(HomematicipBatterySensor(home, device)) for group in home.groups: if isinstance(group, AsyncSecurityGroup): @@ -197,6 +199,24 @@ def device_state_attributes(self): return attr +class HomematicipBatterySensor(HomematicipGenericDevice, BinarySensorDevice): + """Representation of a HomematicIP Cloud low battery sensor.""" + + def __init__(self, home, device): + """Initialize battery sensor.""" + super().__init__(home, device, 'Battery') + + @property + def device_class(self): + """Return the class of this sensor.""" + return 'battery' + + @property + def is_on(self): + """Return true if battery is low.""" + return self._device.lowBat + + class HomematicipSecurityZoneSensorGroup(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud security zone group.""" From 8a0b210f87bafd80d638dae90ecc15e11e797a63 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 2 Apr 2019 20:13:11 +0200 Subject: [PATCH 347/605] Axis discovery updates host address (#22632) * Discovery can update host on existing entries * Add support in device to update host on entry update * Fix tests and listener * Fix hound comment * Fix failing tests from cleanup --- homeassistant/components/axis/__init__.py | 16 ++++----- homeassistant/components/axis/camera.py | 11 +++--- homeassistant/components/axis/config_flow.py | 29 ++++++++++------ homeassistant/components/axis/device.py | 20 ++++++++++- tests/components/axis/test_config_flow.py | 35 ++++++++++++-------- tests/components/axis/test_device.py | 30 +++++++++++++++-- tests/components/axis/test_init.py | 12 +++---- 7 files changed, 104 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/axis/__init__.py b/homeassistant/components/axis/__init__.py index 6082c96863f965..e9ed37477a5a02 100644 --- a/homeassistant/components/axis/__init__.py +++ b/homeassistant/components/axis/__init__.py @@ -4,11 +4,10 @@ from homeassistant import config_entries from homeassistant.const import ( - CONF_DEVICE, CONF_HOST, CONF_NAME, CONF_TRIGGER_TIME, - EVENT_HOMEASSISTANT_STOP) + CONF_DEVICE, CONF_NAME, CONF_TRIGGER_TIME, EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import config_validation as cv -from .config_flow import configured_devices, DEVICE_SCHEMA +from .config_flow import DEVICE_SCHEMA from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN from .device import AxisNetworkDevice, get_device @@ -21,18 +20,17 @@ async def async_setup(hass, config): """Set up for Axis devices.""" - if DOMAIN in config: + if not hass.config_entries.async_entries(DOMAIN) and DOMAIN in config: for device_name, device_config in config[DOMAIN].items(): if CONF_NAME not in device_config: device_config[CONF_NAME] = device_name - if device_config[CONF_HOST] not in configured_devices(hass): - hass.async_create_task(hass.config_entries.flow.async_init( - DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, - data=device_config - )) + hass.async_create_task(hass.config_entries.flow.async_init( + DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, + data=device_config + )) return True diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index 34b6da778a8e6e..ec1d761d3d00d0 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -52,8 +52,7 @@ def __init__(self, config, device): async def async_added_to_hass(self): """Subscribe camera events.""" self.unsub_dispatcher.append(async_dispatcher_connect( - self.hass, 'axis_{}_new_ip'.format(self.device.name), - self._new_ip)) + self.hass, self.device.event_new_address, self._new_address)) self.unsub_dispatcher.append(async_dispatcher_connect( self.hass, self.device.event_reachable, self.update_callback)) @@ -67,10 +66,10 @@ def available(self): """Return True if device is available.""" return self.device.available - def _new_ip(self, host): - """Set new IP for video stream.""" - self._mjpeg_url = AXIS_VIDEO.format(host, self.port) - self._still_image_url = AXIS_IMAGE.format(host, self.port) + def _new_address(self): + """Set new device address for video stream.""" + self._mjpeg_url = AXIS_VIDEO.format(self.device.host, self.port) + self._still_image_url = AXIS_IMAGE.format(self.device.host, self.port) @property def unique_id(self): diff --git a/homeassistant/components/axis/config_flow.py b/homeassistant/components/axis/config_flow.py index 24c286b140a659..54d93f768d20a9 100644 --- a/homeassistant/components/axis/config_flow.py +++ b/homeassistant/components/axis/config_flow.py @@ -40,8 +40,8 @@ @callback def configured_devices(hass): """Return a set of the configured devices.""" - return set(entry.data[CONF_DEVICE][CONF_HOST] for entry - in hass.config_entries.async_entries(DOMAIN)) + return {entry.data[CONF_MAC]: entry for entry + in hass.config_entries.async_entries(DOMAIN)} @config_entries.HANDLERS.register(DOMAIN) @@ -71,9 +71,6 @@ async def async_step_user(self, user_input=None): if user_input is not None: try: - if user_input[CONF_HOST] in configured_devices(self.hass): - raise AlreadyConfigured - self.device_config = { CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT], @@ -84,6 +81,10 @@ async def async_step_user(self, user_input=None): self.serial_number = device.vapix.get_param( VAPIX_SERIAL_NUMBER) + + if self.serial_number in configured_devices(self.hass): + raise AlreadyConfigured + self.model = device.vapix.get_param(VAPIX_MODEL_ID) return await self._create_entry() @@ -142,22 +143,30 @@ async def _create_entry(self): data=data ) + async def _update_entry(self, entry, host): + """Update existing entry if it is the same device.""" + entry.data[CONF_DEVICE][CONF_HOST] = host + self.hass.config_entries.async_update_entry(entry) + async def async_step_discovery(self, discovery_info): """Prepare configuration for a discovered Axis device. This flow is triggered by the discovery component. """ - if discovery_info[CONF_HOST] in configured_devices(self.hass): - return self.async_abort(reason='already_configured') - if discovery_info[CONF_HOST].startswith('169.254'): return self.async_abort(reason='link_local_address') + serialnumber = discovery_info['properties']['macaddress'] + device_entries = configured_devices(self.hass) + + if serialnumber in device_entries: + entry = device_entries[serialnumber] + await self._update_entry(entry, discovery_info[CONF_HOST]) + return self.async_abort(reason='already_configured') + config_file = await self.hass.async_add_executor_job( load_json, self.hass.config.path(CONFIG_FILE)) - serialnumber = discovery_info['properties']['macaddress'] - if serialnumber not in config_file: self.discovery_schema = { vol.Required( diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index 746808e0d915f1..3b3a35f1a2d9ee 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -101,8 +101,26 @@ async def async_setup(self): self.api.enable_events(event_callback=self.async_event_callback) self.api.start() + self.config_entry.add_update_listener(self.async_new_address_callback) + return True + @property + def event_new_address(self): + """Device specific event to signal new device address.""" + return 'axis_new_address_{}'.format(self.serial) + + @staticmethod + async def async_new_address_callback(hass, entry): + """Handle signals of device getting new address. + + This is a static method because a class method (bound method), + can not be used with weak references. + """ + device = hass.data[DOMAIN][entry.data[CONF_MAC]] + device.api.config.host = device.host + async_dispatcher_send(hass, device.event_new_address) + @property def event_reachable(self): """Device specific event to signal a change in connection status.""" @@ -110,7 +128,7 @@ def event_reachable(self): @callback def async_connection_status_callback(self, status): - """Handle signals of gateway connection status. + """Handle signals of device connection status. This is called on every RTSP keep-alive message. Only signal state change if state change is true. diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py index 086c2692d4487c..d78123abb79181 100644 --- a/tests/components/axis/test_config_flow.py +++ b/tests/components/axis/test_config_flow.py @@ -16,7 +16,7 @@ async def test_configured_devices(hass): assert not result entry = MockConfigEntry(domain=axis.DOMAIN, - data={axis.CONF_DEVICE: {axis.CONF_HOST: ''}}) + data={axis.config_flow.CONF_MAC: '1234'}) entry.add_to_hass(hass) result = config_flow.configured_devices(hass) @@ -76,17 +76,21 @@ async def test_flow_fails_already_configured(hass): flow = config_flow.AxisFlowHandler() flow.hass = hass - entry = MockConfigEntry(domain=axis.DOMAIN, data={axis.CONF_DEVICE: { - axis.CONF_HOST: '1.2.3.4' - }}) + entry = MockConfigEntry(domain=axis.DOMAIN, + data={axis.config_flow.CONF_MAC: '1234'}) entry.add_to_hass(hass) - result = await flow.async_step_user(user_input={ - config_flow.CONF_HOST: '1.2.3.4', - config_flow.CONF_USERNAME: 'user', - config_flow.CONF_PASSWORD: 'pass', - config_flow.CONF_PORT: 81 - }) + mock_device = Mock() + mock_device.vapix.get_param.return_value = '1234' + + with patch('homeassistant.components.axis.config_flow.get_device', + return_value=mock_coro(mock_device)): + result = await flow.async_step_user(user_input={ + config_flow.CONF_HOST: '1.2.3.4', + config_flow.CONF_USERNAME: 'user', + config_flow.CONF_PASSWORD: 'pass', + config_flow.CONF_PORT: 81 + }) assert result['errors'] == {'base': 'already_configured'} @@ -220,16 +224,19 @@ async def test_discovery_flow_already_configured(hass): flow = config_flow.AxisFlowHandler() flow.hass = hass - entry = MockConfigEntry(domain=axis.DOMAIN, data={axis.CONF_DEVICE: { - axis.CONF_HOST: '1.2.3.4' - }}) + entry = MockConfigEntry( + domain=axis.DOMAIN, + data={axis.CONF_DEVICE: {axis.config_flow.CONF_HOST: '1.2.3.4'}, + axis.config_flow.CONF_MAC: '1234ABCD'} + ) entry.add_to_hass(hass) result = await flow.async_step_discovery(discovery_info={ config_flow.CONF_HOST: '1.2.3.4', config_flow.CONF_USERNAME: 'user', config_flow.CONF_PASSWORD: 'pass', - config_flow.CONF_PORT: 81 + config_flow.CONF_PORT: 81, + 'properties': {'macaddress': '1234ABCD'} }) print(result) assert result['type'] == 'abort' diff --git a/tests/components/axis/test_device.py b/tests/components/axis/test_device.py index 72d426819c688f..35e350b323c0e9 100644 --- a/tests/components/axis/test_device.py +++ b/tests/components/axis/test_device.py @@ -3,9 +3,10 @@ import pytest -from tests.common import mock_coro +from tests.common import mock_coro, MockConfigEntry from homeassistant.components.axis import device, errors +from homeassistant.components.axis.camera import AxisCamera DEVICE_DATA = { device.CONF_HOST: '1.2.3.4', @@ -16,7 +17,7 @@ ENTRY_OPTIONS = { device.CONF_CAMERA: True, - device.CONF_EVENTS: ['pir'], + device.CONF_EVENTS: True, } ENTRY_CONFIG = { @@ -53,6 +54,31 @@ async def test_device_setup(): (entry, 'binary_sensor') +async def test_device_signal_new_address(hass): + """Successful setup.""" + entry = MockConfigEntry( + domain=device.DOMAIN, data=ENTRY_CONFIG, options=ENTRY_OPTIONS) + + api = Mock() + api.vapix.get_param.return_value = '1234' + + axis_device = device.AxisNetworkDevice(hass, entry) + hass.data[device.DOMAIN] = {axis_device.serial: axis_device} + + with patch.object(device, 'get_device', return_value=mock_coro(api)), \ + patch.object(AxisCamera, '_new_address') as new_address_mock: + await axis_device.async_setup() + await hass.async_block_till_done() + + entry.data[device.CONF_DEVICE][device.CONF_HOST] = '2.3.4.5' + hass.config_entries.async_update_entry(entry, data=entry.data) + await hass.async_block_till_done() + + assert axis_device.host == '2.3.4.5' + assert axis_device.api.config.host == '2.3.4.5' + assert len(new_address_mock.mock_calls) == 1 + + async def test_device_not_accessible(): """Failed setup schedules a retry of setup.""" hass = Mock() diff --git a/tests/components/axis/test_init.py b/tests/components/axis/test_init.py index c1c4c06f6acd5a..737c210b2aa2be 100644 --- a/tests/components/axis/test_init.py +++ b/tests/components/axis/test_init.py @@ -9,30 +9,28 @@ async def test_setup(hass): """Test configured options for a device are loaded via config entry.""" - with patch.object(hass, 'config_entries') as mock_config_entries, \ - patch.object(axis, 'configured_devices', return_value={}): + with patch.object(hass.config_entries, 'flow') as mock_config_flow: assert await async_setup_component(hass, axis.DOMAIN, { axis.DOMAIN: { 'device_name': { - axis.CONF_HOST: '1.2.3.4', + axis.config_flow.CONF_HOST: '1.2.3.4', axis.config_flow.CONF_PORT: 80, } } }) - assert len(mock_config_entries.flow.mock_calls) == 1 + assert len(mock_config_flow.mock_calls) == 1 async def test_setup_device_already_configured(hass): """Test already configured device does not configure a second.""" - with patch.object(hass, 'config_entries') as mock_config_entries, \ - patch.object(axis, 'configured_devices', return_value={'1.2.3.4'}): + with patch.object(hass, 'config_entries') as mock_config_entries: assert await async_setup_component(hass, axis.DOMAIN, { axis.DOMAIN: { 'device_name': { - axis.CONF_HOST: '1.2.3.4' + axis.config_flow.CONF_HOST: '1.2.3.4' } } }) From 8a86a790408b8fdac7ce458fad8b3730919c8d80 Mon Sep 17 00:00:00 2001 From: OleksandrBerchenko Date: Tue, 2 Apr 2019 21:14:46 +0300 Subject: [PATCH 348/605] Add missing properties and scenes support to Osram Lightify (#22597) * Rewrite Osram Lightify component * Update python-lightify version to 1.0.7.2 * Remove unneeded code * 1. Remove changes in light/__init__.py, 2. Set properties to None by default * Fix typo * Implement missing features (including scenes) * Make input parameters to setup_platform standardized --- .../components/osramlightify/light.py | 76 ++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index 59cc2bac5d622a..81b8e2a88ec296 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -26,11 +26,15 @@ CONF_ALLOW_LIGHTIFY_NODES = 'allow_lightify_nodes' CONF_ALLOW_LIGHTIFY_GROUPS = 'allow_lightify_groups' +CONF_ALLOW_LIGHTIFY_SENSORS = 'allow_lightify_sensors' +CONF_ALLOW_LIGHTIFY_SWITCHES = 'allow_lightify_switches' CONF_INTERVAL_LIGHTIFY_STATUS = 'interval_lightify_status' CONF_INTERVAL_LIGHTIFY_CONF = 'interval_lightify_conf' DEFAULT_ALLOW_LIGHTIFY_NODES = True DEFAULT_ALLOW_LIGHTIFY_GROUPS = True +DEFAULT_ALLOW_LIGHTIFY_SENSORS = True +DEFAULT_ALLOW_LIGHTIFY_SWITCHES = True DEFAULT_INTERVAL_LIGHTIFY_STATUS = 5 DEFAULT_INTERVAL_LIGHTIFY_CONF = 3600 @@ -40,6 +44,10 @@ default=DEFAULT_ALLOW_LIGHTIFY_NODES): cv.boolean, vol.Optional(CONF_ALLOW_LIGHTIFY_GROUPS, default=DEFAULT_ALLOW_LIGHTIFY_GROUPS): cv.boolean, + vol.Optional(CONF_ALLOW_LIGHTIFY_SENSORS, + default=DEFAULT_ALLOW_LIGHTIFY_SENSORS): cv.boolean, + vol.Optional(CONF_ALLOW_LIGHTIFY_SWITCHES, + default=DEFAULT_ALLOW_LIGHTIFY_SWITCHES): cv.boolean, vol.Optional(CONF_INTERVAL_LIGHTIFY_STATUS, default=DEFAULT_INTERVAL_LIGHTIFY_STATUS): cv.positive_int, vol.Optional(CONF_INTERVAL_LIGHTIFY_CONF, @@ -50,7 +58,7 @@ DEFAULT_KELVIN = 2700 -def setup_platform(_hass, config, add_entities, _discovery_info=None): +def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Osram Lightify lights.""" import lightify @@ -88,6 +96,12 @@ def update_lights(): if new_lights and config[CONF_ALLOW_LIGHTIFY_NODES]: new_entities = [] for addr, light in new_lights.items(): + if ((light.devicetype().name == 'SENSOR' + and not config[CONF_ALLOW_LIGHTIFY_SENSORS]) or + (light.devicetype().name == 'SWITCH' + and not config[CONF_ALLOW_LIGHTIFY_SWITCHES])): + continue + if addr not in lights: osram_light = OsramLightifyLight(light, update_lights, lights_changed) @@ -105,14 +119,15 @@ def update_groups(): lights_changed = update_lights() try: + bridge.update_scene_list(config[CONF_INTERVAL_LIGHTIFY_CONF]) new_groups = bridge.update_group_list( config[CONF_INTERVAL_LIGHTIFY_CONF]) groups_updated = bridge.groups_updated() except TimeoutError: - _LOGGER.error("Timeout during updating of groups") + _LOGGER.error("Timeout during updating of scenes/groups") return 0 except OSError: - _LOGGER.error("OSError during updating of groups") + _LOGGER.error("OSError during updating of scenes/groups") return 0 if new_groups: @@ -155,11 +170,13 @@ def __init__(self, luminary, update_func, changed): self._supported_features = [] self._effect_list = [] self._is_on = False + self._available = True self._min_mireds = None self._max_mireds = None self._brightness = None self._color_temp = None self._rgb_color = None + self._device_attributes = None self.update_static_attributes() self.update_dynamic_attributes() @@ -241,6 +258,16 @@ def unique_id(self): """Return a unique ID.""" return self._unique_id + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + return self._device_attributes + + @property + def available(self): + """Return True if entity is available.""" + return self._available + def play_effect(self, effect, transition): """Play selected effect.""" if effect == EFFECT_RANDOM: @@ -308,6 +335,8 @@ def update_static_attributes(self): def update_dynamic_attributes(self): """Update dynamic attributes of the luminary.""" self._is_on = self._luminary.on() + self._available = (self._luminary.reachable() and + not self._luminary.deleted()) if self._supported_features & SUPPORT_BRIGHTNESS: self._brightness = int(self._luminary.lum() * 2.55) @@ -333,6 +362,17 @@ def _get_unique_id(self): """Get a unique ID.""" return self._luminary.addr() + def update_static_attributes(self): + """Update static attributes of the luminary.""" + super().update_static_attributes() + attrs = {'device_type': '{} ({})'.format(self._luminary.type_id(), + self._luminary.devicename()), + 'firmware_version': self._luminary.version()} + if self._luminary.devicetype().name == 'SENSOR': + attrs['sensor_values'] = self._luminary.raw_values() + + self._device_attributes = attrs + class OsramLightifyGroup(Luminary): """Representation of an Osram Lightify Group.""" @@ -347,3 +387,33 @@ def _get_unique_id(self): # For now keeping it as is for backward compatibility with existing # users. return '{}'.format(self._luminary.lights()) + + def _get_supported_features(self): + """Get list of supported features.""" + features = super()._get_supported_features() + if self._luminary.scenes(): + features = features | SUPPORT_EFFECT + + return features + + def _get_effect_list(self): + """Get list of supported effects.""" + effects = super()._get_effect_list() + effects.extend(self._luminary.scenes()) + return sorted(effects) + + def play_effect(self, effect, transition): + """Play selected effect.""" + if super().play_effect(effect, transition): + return True + + if effect in self._luminary.scenes(): + self._luminary.activate_scene(effect) + return True + + return False + + def update_static_attributes(self): + """Update static attributes of the luminary.""" + super().update_static_attributes() + self._device_attributes = {'lights': self._luminary.light_names()} From 471afb4702a09933e792971f1adfe2b70000fdfa Mon Sep 17 00:00:00 2001 From: Alex Bahm Date: Tue, 2 Apr 2019 11:25:58 -0700 Subject: [PATCH 349/605] Add color support to emulated hue (#19590) * [Hue API] Add color support Adds color support to the hue api (specifically hue/saturation). Switched from using a tuple to convey state internally to using a dict to make adding new fields easier. * [Hue API] Add unit test for color support --- .../components/emulated_hue/hue_api.py | 255 +++++++++++------- tests/components/emulated_hue/test_hue_api.py | 37 ++- 2 files changed, 197 insertions(+), 95 deletions(-) diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 4c329cac28fd48..44a9c6e53ef2b4 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -4,42 +4,42 @@ from aiohttp import web from homeassistant import core -from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_TEMPERATURE, SERVICE_TURN_OFF, SERVICE_TURN_ON, - SERVICE_VOLUME_SET, SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, STATE_ON, - STATE_OFF, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, ATTR_SUPPORTED_FEATURES -) -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS -) +from homeassistant.components import ( + climate, cover, fan, light, media_player, scene, script) from homeassistant.components.climate.const import ( - SERVICE_SET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE -) -from homeassistant.components.media_player.const import ( - ATTR_MEDIA_VOLUME_LEVEL, SUPPORT_VOLUME_SET, -) -from homeassistant.components.fan import ( - ATTR_SPEED, SUPPORT_SET_SPEED, SPEED_OFF, SPEED_LOW, - SPEED_MEDIUM, SPEED_HIGH -) - + SERVICE_SET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE) from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_POSITION, SERVICE_SET_COVER_POSITION, - SUPPORT_SET_POSITION -) - -from homeassistant.components import ( - climate, cover, fan, media_player, light, script, scene -) - + SUPPORT_SET_POSITION) +from homeassistant.components.fan import ( + ATTR_SPEED, SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, + SUPPORT_SET_SPEED) from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.const import KEY_REAL_IP +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR) +from homeassistant.components.media_player.const import ( + ATTR_MEDIA_VOLUME_LEVEL, SUPPORT_VOLUME_SET) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + HTTP_BAD_REQUEST, HTTP_NOT_FOUND, SERVICE_CLOSE_COVER, SERVICE_OPEN_COVER, + SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_SET, STATE_OFF, STATE_ON) from homeassistant.util.network import is_local _LOGGER = logging.getLogger(__name__) HUE_API_STATE_ON = 'on' HUE_API_STATE_BRI = 'bri' +HUE_API_STATE_HUE = 'hue' +HUE_API_STATE_SAT = 'sat' + +HUE_API_STATE_HUE_MAX = 65535.0 +HUE_API_STATE_SAT_MAX = 254.0 +HUE_API_STATE_BRI_MAX = 255.0 + +STATE_BRIGHTNESS = HUE_API_STATE_BRI +STATE_HUE = HUE_API_STATE_HUE +STATE_SATURATION = HUE_API_STATE_SAT class HueUsernameView(HomeAssistantView): @@ -140,11 +140,11 @@ def get(self, request, username): for entity in hass.states.async_all(): if self.config.is_entity_exposed(entity): - state, brightness = get_entity_state(self.config, entity) + state = get_entity_state(self.config, entity) number = self.config.entity_id_to_number(entity.entity_id) - json_response[number] = entity_to_json( - self.config, entity, state, brightness) + json_response[number] = entity_to_json(self.config, + entity, state) return self.json(json_response) @@ -179,9 +179,9 @@ def get(self, request, username, entity_id): _LOGGER.error('Entity not exposed: %s', entity_id) return web.Response(text="Entity not exposed", status=404) - state, brightness = get_entity_state(self.config, entity) + state = get_entity_state(self.config, entity) - json_response = entity_to_json(self.config, entity, state, brightness) + json_response = entity_to_json(self.config, entity, state) return self.json(json_response) @@ -234,8 +234,6 @@ async def put(self, request, username, entity_number): _LOGGER.error('Unable to parse data: %s', request_json) return web.Response(text="Bad request", status=400) - result, brightness = parsed - # Choose general HA domain domain = core.DOMAIN @@ -243,7 +241,7 @@ async def put(self, request, username, entity_number): turn_on_needed = False # Convert the resulting "on" status into the service we need to call - service = SERVICE_TURN_ON if result else SERVICE_TURN_OFF + service = SERVICE_TURN_ON if parsed[STATE_ON] else SERVICE_TURN_OFF # Construct what we need to send to the service data = {ATTR_ENTITY_ID: entity_id} @@ -252,18 +250,32 @@ async def put(self, request, username, entity_number): entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) if entity.domain == light.DOMAIN: - if entity_features & SUPPORT_BRIGHTNESS: - if brightness is not None: - data[ATTR_BRIGHTNESS] = brightness + if parsed[STATE_ON]: + if entity_features & SUPPORT_BRIGHTNESS: + if parsed[STATE_BRIGHTNESS] is not None: + data[ATTR_BRIGHTNESS] = parsed[STATE_BRIGHTNESS] + if entity_features & SUPPORT_COLOR: + if parsed[STATE_HUE] is not None: + if parsed[STATE_SATURATION]: + sat = parsed[STATE_SATURATION] + else: + sat = 0 + hue = parsed[STATE_HUE] + + # Convert hs values to hass hs values + sat = int((sat / HUE_API_STATE_SAT_MAX) * 100) + hue = int((hue / HUE_API_STATE_HUE_MAX) * 360) + + data[ATTR_HS_COLOR] = (hue, sat) # If the requested entity is a script add some variables elif entity.domain == script.DOMAIN: data['variables'] = { - 'requested_state': STATE_ON if result else STATE_OFF + 'requested_state': STATE_ON if parsed[STATE_ON] else STATE_OFF } - if brightness is not None: - data['variables']['requested_level'] = brightness + if parsed[STATE_BRIGHTNESS] is not None: + data['variables']['requested_level'] = parsed[STATE_BRIGHTNESS] # If the requested entity is a climate, set the temperature elif entity.domain == climate.DOMAIN: @@ -272,20 +284,21 @@ async def put(self, request, username, entity_number): service = None if entity_features & SUPPORT_TARGET_TEMPERATURE: - if brightness is not None: + if parsed[STATE_BRIGHTNESS] is not None: domain = entity.domain service = SERVICE_SET_TEMPERATURE - data[ATTR_TEMPERATURE] = brightness + data[ATTR_TEMPERATURE] = parsed[STATE_BRIGHTNESS] # If the requested entity is a media player, convert to volume elif entity.domain == media_player.DOMAIN: if entity_features & SUPPORT_VOLUME_SET: - if brightness is not None: + if parsed[STATE_BRIGHTNESS] is not None: turn_on_needed = True domain = entity.domain service = SERVICE_VOLUME_SET # Convert 0-100 to 0.0-1.0 - data[ATTR_MEDIA_VOLUME_LEVEL] = brightness / 100.0 + data[ATTR_MEDIA_VOLUME_LEVEL] = \ + parsed[STATE_BRIGHTNESS] / 100.0 # If the requested entity is a cover, convert to open_cover/close_cover elif entity.domain == cover.DOMAIN: @@ -296,17 +309,18 @@ async def put(self, request, username, entity_number): service = SERVICE_CLOSE_COVER if entity_features & SUPPORT_SET_POSITION: - if brightness is not None: + if parsed[STATE_BRIGHTNESS] is not None: domain = entity.domain service = SERVICE_SET_COVER_POSITION - data[ATTR_POSITION] = brightness + data[ATTR_POSITION] = parsed[STATE_BRIGHTNESS] # If the requested entity is a fan, convert to speed elif entity.domain == fan.DOMAIN: if entity_features & SUPPORT_SET_SPEED: - if brightness is not None: + if parsed[STATE_BRIGHTNESS] is not None: domain = entity.domain # Convert 0-100 to a fan speed + brightness = parsed[STATE_BRIGHTNESS] if brightness == 0: data[ATTR_SPEED] = SPEED_OFF elif 0 < brightness <= 33.3: @@ -325,7 +339,7 @@ async def put(self, request, username, entity_number): # they'll map to "on". Thus, instead of reporting its actual # status, we report what Alexa will want to see, which is the same # as the actual requested command. - config.cached_states[entity_id] = (result, brightness) + config.cached_states[entity_id] = parsed # Separate call to turn on needed if turn_on_needed: @@ -338,73 +352,120 @@ async def put(self, request, username, entity_number): domain, service, data, blocking=True)) json_response = \ - [create_hue_success_response(entity_id, HUE_API_STATE_ON, result)] + [create_hue_success_response( + entity_id, HUE_API_STATE_ON, parsed[STATE_ON])] - if brightness is not None: + if parsed[STATE_BRIGHTNESS] is not None: + json_response.append(create_hue_success_response( + entity_id, HUE_API_STATE_BRI, parsed[STATE_BRIGHTNESS])) + if parsed[STATE_HUE] is not None: + json_response.append(create_hue_success_response( + entity_id, HUE_API_STATE_HUE, parsed[STATE_HUE])) + if parsed[STATE_SATURATION] is not None: json_response.append(create_hue_success_response( - entity_id, HUE_API_STATE_BRI, brightness)) + entity_id, HUE_API_STATE_SAT, parsed[STATE_SATURATION])) return self.json(json_response) def parse_hue_api_put_light_body(request_json, entity): """Parse the body of a request to change the state of a light.""" + data = { + STATE_BRIGHTNESS: None, + STATE_HUE: None, + STATE_ON: False, + STATE_SATURATION: None, + } + + # Make sure the entity actually supports brightness + entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + if HUE_API_STATE_ON in request_json: if not isinstance(request_json[HUE_API_STATE_ON], bool): return None - if request_json['on']: + if request_json[HUE_API_STATE_ON]: # Echo requested device be turned on - brightness = None - report_brightness = False - result = True + data[STATE_BRIGHTNESS] = None + data[STATE_ON] = True else: # Echo requested device be turned off - brightness = None - report_brightness = False - result = False + data[STATE_BRIGHTNESS] = None + data[STATE_ON] = False + + if HUE_API_STATE_HUE in request_json: + try: + # Clamp brightness from 0 to 65535 + data[STATE_HUE] = \ + max(0, min(int(request_json[HUE_API_STATE_HUE]), + HUE_API_STATE_HUE_MAX)) + except ValueError: + return None + + if HUE_API_STATE_SAT in request_json: + try: + # Clamp saturation from 0 to 254 + data[STATE_SATURATION] = \ + max(0, min(int(request_json[HUE_API_STATE_SAT]), + HUE_API_STATE_SAT_MAX)) + except ValueError: + return None if HUE_API_STATE_BRI in request_json: try: # Clamp brightness from 0 to 255 - brightness = \ - max(0, min(int(request_json[HUE_API_STATE_BRI]), 255)) + data[STATE_BRIGHTNESS] = \ + max(0, min(int(request_json[HUE_API_STATE_BRI]), + HUE_API_STATE_BRI_MAX)) except ValueError: return None - # Make sure the entity actually supports brightness - entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - if entity.domain == light.DOMAIN: - if entity_features & SUPPORT_BRIGHTNESS: - report_brightness = True - result = (brightness > 0) + data[STATE_ON] = (data[STATE_BRIGHTNESS] > 0) + if not entity_features & SUPPORT_BRIGHTNESS: + data[STATE_BRIGHTNESS] = None elif entity.domain == scene.DOMAIN: - brightness = None - report_brightness = False - result = True + data[STATE_BRIGHTNESS] = None + data[STATE_ON] = True elif entity.domain in [ script.DOMAIN, media_player.DOMAIN, fan.DOMAIN, cover.DOMAIN, climate.DOMAIN]: # Convert 0-255 to 0-100 - level = brightness / 255 * 100 - brightness = round(level) - report_brightness = True - result = True + level = (data[STATE_BRIGHTNESS] / HUE_API_STATE_BRI_MAX) * 100 + data[STATE_BRIGHTNESS] = round(level) + data[STATE_ON] = True - return (result, brightness) if report_brightness else (result, None) + return data def get_entity_state(config, entity): """Retrieve and convert state and brightness values for an entity.""" cached_state = config.cached_states.get(entity.entity_id, None) + data = { + STATE_BRIGHTNESS: None, + STATE_HUE: None, + STATE_ON: False, + STATE_SATURATION: None + } if cached_state is None: - final_state = entity.state != STATE_OFF - final_brightness = entity.attributes.get( - ATTR_BRIGHTNESS, 255 if final_state else 0) + data[STATE_ON] = entity.state != STATE_OFF + if data[STATE_ON]: + data[STATE_BRIGHTNESS] = entity.attributes.get(ATTR_BRIGHTNESS) + hue_sat = entity.attributes.get(ATTR_HS_COLOR, None) + if hue_sat is not None: + hue = hue_sat[0] + sat = hue_sat[1] + # convert hass hs values back to hue hs values + data[STATE_HUE] = int((hue / 360.0) * HUE_API_STATE_HUE_MAX) + data[STATE_SATURATION] = \ + int((sat / 100.0) * HUE_API_STATE_SAT_MAX) + else: + data[STATE_BRIGHTNESS] = 0 + data[STATE_HUE] = 0 + data[STATE_SATURATION] = 0 # Make sure the entity actually supports brightness entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) @@ -416,41 +477,53 @@ def get_entity_state(config, entity): elif entity.domain == climate.DOMAIN: temperature = entity.attributes.get(ATTR_TEMPERATURE, 0) # Convert 0-100 to 0-255 - final_brightness = round(temperature * 255 / 100) + data[STATE_BRIGHTNESS] = round(temperature * 255 / 100) elif entity.domain == media_player.DOMAIN: level = entity.attributes.get( - ATTR_MEDIA_VOLUME_LEVEL, 1.0 if final_state else 0.0) + ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0) # Convert 0.0-1.0 to 0-255 - final_brightness = round(min(1.0, level) * 255) + data[STATE_BRIGHTNESS] = \ + round(min(1.0, level) * HUE_API_STATE_BRI_MAX) elif entity.domain == fan.DOMAIN: speed = entity.attributes.get(ATTR_SPEED, 0) # Convert 0.0-1.0 to 0-255 - final_brightness = 0 + data[STATE_BRIGHTNESS] = 0 if speed == SPEED_LOW: - final_brightness = 85 + data[STATE_BRIGHTNESS] = 85 elif speed == SPEED_MEDIUM: - final_brightness = 170 + data[STATE_BRIGHTNESS] = 170 elif speed == SPEED_HIGH: - final_brightness = 255 + data[STATE_BRIGHTNESS] = 255 elif entity.domain == cover.DOMAIN: level = entity.attributes.get(ATTR_CURRENT_POSITION, 0) - final_brightness = round(level / 100 * 255) + data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX) else: - final_state, final_brightness = cached_state + data = cached_state # Make sure brightness is valid - if final_brightness is None: - final_brightness = 255 if final_state else 0 + if data[STATE_BRIGHTNESS] is None: + data[STATE_BRIGHTNESS] = 255 if data[STATE_ON] else 0 + # Make sure hue/saturation are valid + if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None): + data[STATE_HUE] = 0 + data[STATE_SATURATION] = 0 + + # If the light is off, set the color to off + if data[STATE_BRIGHTNESS] == 0: + data[STATE_HUE] = 0 + data[STATE_SATURATION] = 0 - return (final_state, final_brightness) + return data -def entity_to_json(config, entity, is_on=None, brightness=None): +def entity_to_json(config, entity, state): """Convert an entity to its Hue bridge JSON representation.""" return { 'state': { - HUE_API_STATE_ON: is_on, - HUE_API_STATE_BRI: brightness, + HUE_API_STATE_ON: state[STATE_ON], + HUE_API_STATE_BRI: state[STATE_BRIGHTNESS], + HUE_API_STATE_HUE: state[STATE_HUE], + HUE_API_STATE_SAT: state[STATE_SATURATION], 'reachable': True }, 'type': 'Dimmable light', diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 08001b0ebab522..3348fdfe87bc95 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -13,7 +13,8 @@ fan, http, light, script, emulated_hue, media_player, cover, climate) from homeassistant.components.emulated_hue import Config from homeassistant.components.emulated_hue.hue_api import ( - HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView, HueOneLightStateView, + HUE_API_STATE_ON, HUE_API_STATE_BRI, HUE_API_STATE_HUE, HUE_API_STATE_SAT, + HueUsernameView, HueOneLightStateView, HueAllLightsStateView, HueOneLightChangeView, HueAllGroupsStateView) from homeassistant.const import STATE_ON, STATE_OFF @@ -221,12 +222,13 @@ def test_discover_lights(hue_client): @asyncio.coroutine def test_get_light_state(hass_hue, hue_client): """Test the getting of light state.""" - # Turn office light on and set to 127 brightness + # Turn office light on and set to 127 brightness, and set light color yield from hass_hue.services.async_call( light.DOMAIN, const.SERVICE_TURN_ON, { const.ATTR_ENTITY_ID: 'light.ceiling_lights', - light.ATTR_BRIGHTNESS: 127 + light.ATTR_BRIGHTNESS: 127, + light.ATTR_RGB_COLOR: (1, 2, 7) }, blocking=True) @@ -235,6 +237,8 @@ def test_get_light_state(hass_hue, hue_client): assert office_json['state'][HUE_API_STATE_ON] is True assert office_json['state'][HUE_API_STATE_BRI] == 127 + assert office_json['state'][HUE_API_STATE_HUE] == 41869 + assert office_json['state'][HUE_API_STATE_SAT] == 217 # Check all lights view result = yield from hue_client.get('/api/username/lights') @@ -261,6 +265,8 @@ def test_get_light_state(hass_hue, hue_client): assert office_json['state'][HUE_API_STATE_ON] is False assert office_json['state'][HUE_API_STATE_BRI] == 0 + assert office_json['state'][HUE_API_STATE_HUE] == 0 + assert office_json['state'][HUE_API_STATE_SAT] == 0 # Make sure bedroom light isn't accessible yield from perform_get_light_state( @@ -287,6 +293,19 @@ def test_put_light_state(hass_hue, hue_client): assert ceiling_lights.state == STATE_ON assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 153 + # update light state through api + yield from perform_put_light_state( + hass_hue, hue_client, + 'light.ceiling_lights', True, + hue=4369, saturation=127, brightness=123) + + # go through api to get the state back + ceiling_json = yield from perform_get_light_state( + hue_client, 'light.ceiling_lights', 200) + assert ceiling_json['state'][HUE_API_STATE_BRI] == 123 + assert ceiling_json['state'][HUE_API_STATE_HUE] == 4369 + assert ceiling_json['state'][HUE_API_STATE_SAT] == 127 + # Go through the API to turn it off ceiling_result = yield from perform_put_light_state( hass_hue, hue_client, @@ -302,6 +321,11 @@ def test_put_light_state(hass_hue, hue_client): # Check to make sure the state changed ceiling_lights = hass_hue.states.get('light.ceiling_lights') assert ceiling_lights.state == STATE_OFF + ceiling_json = yield from perform_get_light_state( + hue_client, 'light.ceiling_lights', 200) + assert ceiling_json['state'][HUE_API_STATE_BRI] == 0 + assert ceiling_json['state'][HUE_API_STATE_HUE] == 0 + assert ceiling_json['state'][HUE_API_STATE_SAT] == 0 # Make sure we can't change the bedroom light state bedroom_result = yield from perform_put_light_state( @@ -706,7 +730,8 @@ def perform_get_light_state(client, entity_id, expected_status): @asyncio.coroutine def perform_put_light_state(hass_hue, client, entity_id, is_on, - brightness=None, content_type='application/json'): + brightness=None, content_type='application/json', + hue=None, saturation=None): """Test the setting of a light state.""" req_headers = {'Content-Type': content_type} @@ -714,6 +739,10 @@ def perform_put_light_state(hass_hue, client, entity_id, is_on, if brightness is not None: data[HUE_API_STATE_BRI] = brightness + if hue is not None: + data[HUE_API_STATE_HUE] = hue + if saturation is not None: + data[HUE_API_STATE_SAT] = saturation result = yield from client.put( '/api/username/lights/{}/state'.format(entity_id), headers=req_headers, From 5cb69cf1631b4145da68c13df0234a60c0ddb9d7 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 17:43:29 -0700 Subject: [PATCH 350/605] Add trusted networks deprecating warning (#22487) * Add trusted networks deprecating warning * Update auth.py * Update auth.py * Update auth.py * Update auth.py * Tweak --- homeassistant/components/http/auth.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 4736ef123913ae..7b8508894ce73d 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,6 +190,12 @@ async def auth_middleware(request, handler): elif (trusted_networks and await async_validate_trusted_networks(request)): + _LOGGER.warning( + 'Access from trusted networks without auth token is going to ' + 'be removed in Home Assistant 0.96. Configure the trusted ' + 'networks auth provider or use long-lived access tokens to ' + 'access %s from %s', + request.path, request[KEY_REAL_IP]) authenticated = True elif (support_legacy and HTTP_HEADER_HA_AUTH in request.headers and From 6f345c55c966b761b67671cf3209e07e84f6cee1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 1 Apr 2019 14:16:16 +0200 Subject: [PATCH 351/605] Hass.io ingress (#22505) * Fix API stream of snapshot / Add ingress * fix lint * Fix stream handling * Cleanup api handling * fix typing * Set proxy header * Use header constant * Enable the ingress setup * fix lint * Fix name * Fix tests * fix lint * forward params * Add tests for ingress * Cleanup cookie handling with aiohttp 3.5 * Add more tests * Fix tests * Fix lint * Fix header handling for steam * forward header too * fix lint * fix flake --- homeassistant/components/hassio/__init__.py | 4 + homeassistant/components/hassio/const.py | 7 +- homeassistant/components/hassio/http.py | 79 +++++--- homeassistant/components/hassio/ingress.py | 210 ++++++++++++++++++++ tests/components/hassio/test_http.py | 86 +++----- tests/components/hassio/test_ingress.py | 162 +++++++++++++++ tests/components/hassio/test_init.py | 2 +- tests/test_util/aiohttp.py | 2 +- 8 files changed, 461 insertions(+), 91 deletions(-) create mode 100644 homeassistant/components/hassio/ingress.py create mode 100644 tests/components/hassio/test_ingress.py diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 073974200a0935..e8d04b1596d980 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -20,6 +20,7 @@ from .discovery import async_setup_discovery from .handler import HassIO, HassioAPIError from .http import HassIOView +from .ingress import async_setup_ingress _LOGGER = logging.getLogger(__name__) @@ -270,4 +271,7 @@ async def async_handle_core_service(call): # Init auth Hass.io feature async_setup_auth(hass) + # Init ingress Hass.io feature + async_setup_ingress(hass, host) + return True diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index 964f94bfb41c63..e4132562c31e39 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -9,6 +9,7 @@ ATTR_USERNAME = 'username' ATTR_PASSWORD = 'password' -X_HASSIO = 'X-HASSIO-KEY' -X_HASS_USER_ID = 'X-HASS-USER-ID' -X_HASS_IS_ADMIN = 'X-HASS-IS-ADMIN' +X_HASSIO = 'X-Hassio-Key' +X_INGRESS_PATH = "X-Ingress-Path" +X_HASS_USER_ID = 'X-Hass-User-ID' +X_HASS_IS_ADMIN = 'X-Hass-Is-Admin' diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index 01ded9ca11dc75..7284004d72f9d4 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -3,10 +3,11 @@ import logging import os import re +from typing import Dict, Union import aiohttp from aiohttp import web -from aiohttp.hdrs import CONTENT_TYPE +from aiohttp.hdrs import CONTENT_TYPE, CONTENT_LENGTH from aiohttp.web_exceptions import HTTPBadGateway import async_timeout @@ -20,7 +21,8 @@ NO_TIMEOUT = re.compile( r'^(?:' r'|homeassistant/update' - r'|host/update' + r'|hassos/update' + r'|hassos/update/cli' r'|supervisor/update' r'|addons/[^/]+/(?:update|install|rebuild)' r'|snapshots/.+/full' @@ -44,25 +46,26 @@ class HassIOView(HomeAssistantView): url = "/api/hassio/{path:.+}" requires_auth = False - def __init__(self, host, websession): + def __init__(self, host: str, websession: aiohttp.ClientSession): """Initialize a Hass.io base view.""" self._host = host self._websession = websession - async def _handle(self, request, path): + async def _handle( + self, request: web.Request, path: str + ) -> Union[web.Response, web.StreamResponse]: """Route data to Hass.io.""" if _need_auth(path) and not request[KEY_AUTHENTICATED]: return web.Response(status=401) - client = await self._command_proxy(path, request) - - data = await client.read() - return _create_response(client, data) + return await self._command_proxy(path, request) get = _handle post = _handle - async def _command_proxy(self, path, request): + async def _command_proxy( + self, path: str, request: web.Request + ) -> Union[web.Response, web.StreamResponse]: """Return a client request with proxy origin for Hass.io supervisor. This method is a coroutine. @@ -71,29 +74,38 @@ async def _command_proxy(self, path, request): hass = request.app['hass'] data = None - headers = { - X_HASSIO: os.environ.get('HASSIO_TOKEN', ""), - } - user = request.get('hass_user') - if user is not None: - headers[X_HASS_USER_ID] = request['hass_user'].id - headers[X_HASS_IS_ADMIN] = str(int(request['hass_user'].is_admin)) + headers = _init_header(request) try: with async_timeout.timeout(10, loop=hass.loop): data = await request.read() - if data: - headers[CONTENT_TYPE] = request.content_type - else: - data = None method = getattr(self._websession, request.method.lower()) client = await method( "http://{}/{}".format(self._host, path), data=data, headers=headers, timeout=read_timeout ) + print(client.headers) + + # Simple request + if int(client.headers.get(CONTENT_LENGTH, 0)) < 4194000: + # Return Response + body = await client.read() + return web.Response( + content_type=client.content_type, + status=client.status, + body=body, + ) + + # Stream response + response = web.StreamResponse(status=client.status) + response.content_type = client.content_type - return client + await response.prepare(request) + async for data in client.content.iter_chunked(4096): + await response.write(data) + + return response except aiohttp.ClientError as err: _LOGGER.error("Client error on api %s request %s", path, err) @@ -104,23 +116,30 @@ async def _command_proxy(self, path, request): raise HTTPBadGateway() -def _create_response(client, data): - """Convert a response from client request.""" - return web.Response( - body=data, - status=client.status, - content_type=client.content_type, - ) +def _init_header(request: web.Request) -> Dict[str, str]: + """Create initial header.""" + headers = { + X_HASSIO: os.environ.get('HASSIO_TOKEN', ""), + CONTENT_TYPE: request.content_type, + } + + # Add user data + user = request.get('hass_user') + if user is not None: + headers[X_HASS_USER_ID] = request['hass_user'].id + headers[X_HASS_IS_ADMIN] = str(int(request['hass_user'].is_admin)) + + return headers -def _get_timeout(path): +def _get_timeout(path: str) -> int: """Return timeout for a URL path.""" if NO_TIMEOUT.match(path): return 0 return 300 -def _need_auth(path): +def _need_auth(path: str) -> bool: """Return if a path need authentication.""" if NO_AUTH.match(path): return False diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py new file mode 100644 index 00000000000000..6c1ef389712dff --- /dev/null +++ b/homeassistant/components/hassio/ingress.py @@ -0,0 +1,210 @@ +"""Hass.io Add-on ingress service.""" +import asyncio +from ipaddress import ip_address +import os +from typing import Dict, Union + +import aiohttp +from aiohttp import web +from aiohttp import hdrs +from aiohttp.web_exceptions import HTTPBadGateway +from multidict import CIMultiDict + +from homeassistant.core import callback +from homeassistant.components.http import HomeAssistantView +from homeassistant.helpers.typing import HomeAssistantType + +from .const import X_HASSIO, X_INGRESS_PATH + + +@callback +def async_setup_ingress(hass: HomeAssistantType, host: str): + """Auth setup.""" + websession = hass.helpers.aiohttp_client.async_get_clientsession() + + hassio_ingress = HassIOIngress(host, websession) + hass.http.register_view(hassio_ingress) + + +class HassIOIngress(HomeAssistantView): + """Hass.io view to handle base part.""" + + name = "api:hassio:ingress" + url = "/api/hassio_ingress/{addon}/{path:.+}" + requires_auth = False + + def __init__(self, host: str, websession: aiohttp.ClientSession): + """Initialize a Hass.io ingress view.""" + self._host = host + self._websession = websession + + def _create_url(self, addon: str, path: str) -> str: + """Create URL to service.""" + return "http://{}/addons/{}/web/{}".format(self._host, addon, path) + + async def _handle( + self, request: web.Request, addon: str, path: str + ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: + """Route data to Hass.io ingress service.""" + try: + # Websocket + if _is_websocket(request): + return await self._handle_websocket(request, addon, path) + + # Request + return await self._handle_request(request, addon, path) + + except aiohttp.ClientError: + pass + + raise HTTPBadGateway() from None + + get = _handle + post = _handle + put = _handle + delete = _handle + + async def _handle_websocket( + self, request: web.Request, addon: str, path: str + ) -> web.WebSocketResponse: + """Ingress route for websocket.""" + ws_server = web.WebSocketResponse() + await ws_server.prepare(request) + + url = self._create_url(addon, path) + source_header = _init_header(request, addon) + + # Start proxy + async with self._websession.ws_connect( + url, headers=source_header + ) as ws_client: + # Proxy requests + await asyncio.wait( + [ + _websocket_forward(ws_server, ws_client), + _websocket_forward(ws_client, ws_server), + ], + return_when=asyncio.FIRST_COMPLETED + ) + + return ws_server + + async def _handle_request( + self, request: web.Request, addon: str, path: str + ) -> Union[web.Response, web.StreamResponse]: + """Ingress route for request.""" + url = self._create_url(addon, path) + data = await request.read() + source_header = _init_header(request, addon) + + async with self._websession.request( + request.method, url, headers=source_header, + params=request.query, data=data, cookies=request.cookies + ) as result: + headers = _response_header(result) + + # Simple request + if hdrs.CONTENT_LENGTH in result.headers and \ + int(result.headers.get(hdrs.CONTENT_LENGTH, 0)) < 4194000: + # Return Response + body = await result.read() + return web.Response( + headers=headers, + status=result.status, + body=body + ) + + # Stream response + response = web.StreamResponse( + status=result.status, headers=headers) + response.content_type = result.content_type + + try: + await response.prepare(request) + async for data in result.content: + await response.write(data) + + except (aiohttp.ClientError, aiohttp.ClientPayloadError): + pass + + return response + + +def _init_header( + request: web.Request, addon: str +) -> Union[CIMultiDict, Dict[str, str]]: + """Create initial header.""" + headers = {} + + # filter flags + for name, value in request.headers.items(): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE): + continue + headers[name] = value + + # Inject token / cleanup later on Supervisor + headers[X_HASSIO] = os.environ.get('HASSIO_TOKEN', "") + + # Ingress information + headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(addon) + + # Set X-Forwarded-For + forward_for = request.headers.get(hdrs.X_FORWARDED_FOR) + connected_ip = ip_address(request.transport.get_extra_info('peername')[0]) + if forward_for: + forward_for = "{}, {!s}".format(forward_for, connected_ip) + else: + forward_for = "{!s}".format(connected_ip) + headers[hdrs.X_FORWARDED_FOR] = forward_for + + # Set X-Forwarded-Host + forward_host = request.headers.get(hdrs.X_FORWARDED_HOST) + if not forward_host: + forward_host = request.host + headers[hdrs.X_FORWARDED_HOST] = forward_host + + # Set X-Forwarded-Proto + forward_proto = request.headers.get(hdrs.X_FORWARDED_PROTO) + if not forward_proto: + forward_proto = request.url.scheme + headers[hdrs.X_FORWARDED_PROTO] = forward_proto + + return headers + + +def _response_header(response: aiohttp.ClientResponse) -> Dict[str, str]: + """Create response header.""" + headers = {} + + for name, value in response.headers.items(): + if name in (hdrs.TRANSFER_ENCODING, hdrs.CONTENT_LENGTH, + hdrs.CONTENT_TYPE): + continue + headers[name] = value + + return headers + + +def _is_websocket(request: web.Request) -> bool: + """Return True if request is a websocket.""" + headers = request.headers + + if headers.get(hdrs.CONNECTION) == "Upgrade" and \ + headers.get(hdrs.UPGRADE) == "websocket": + return True + return False + + +async def _websocket_forward(ws_from, ws_to): + """Handle websocket message directly.""" + async for msg in ws_from: + if msg.type == aiohttp.WSMsgType.TEXT: + await ws_to.send_str(msg.data) + elif msg.type == aiohttp.WSMsgType.BINARY: + await ws_to.send_bytes(msg.data) + elif msg.type == aiohttp.WSMsgType.PING: + await ws_to.ping() + elif msg.type == aiohttp.WSMsgType.PONG: + await ws_to.pong() + elif ws_to.closed: + await ws_to.close(code=ws_to.close_code, message=msg.extra) diff --git a/tests/components/hassio/test_http.py b/tests/components/hassio/test_http.py index 3f58c6e697e3b0..3a58048735bf97 100644 --- a/tests/components/hassio/test_http.py +++ b/tests/components/hassio/test_http.py @@ -1,29 +1,22 @@ """The tests for the hassio component.""" import asyncio -from unittest.mock import patch, Mock, MagicMock +from unittest.mock import patch import pytest from homeassistant.const import HTTP_HEADER_HA_AUTH -from tests.common import mock_coro from . import API_PASSWORD @asyncio.coroutine -def test_forward_request(hassio_client): +def test_forward_request(hassio_client, aioclient_mock): """Test fetching normal path.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http' - '._create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.post('/api/hassio/beer', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) + aioclient_mock.post("http://127.0.0.1/beer", text="response") + + resp = yield from hassio_client.post('/api/hassio/beer', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) # Check we got right response assert resp.status == 200 @@ -31,8 +24,7 @@ def test_forward_request(hassio_client): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine @@ -55,18 +47,13 @@ def test_auth_required_forward_request(hassio_noauth_client, build_type): 'app/index.html', 'app/hassio-app.html', 'app/index.html', 'app/hassio-app.html', 'app/some-chunk.js', 'app/app.js', ]) -def test_forward_request_no_auth_for_panel(hassio_client, build_type): +def test_forward_request_no_auth_for_panel( + hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get( - '/api/hassio/{}'.format(build_type)) + aioclient_mock.get( + "http://127.0.0.1/{}".format(build_type), text="response") + + resp = yield from hassio_client.get('/api/hassio/{}'.format(build_type)) # Check we got right response assert resp.status == 200 @@ -74,22 +61,16 @@ def test_forward_request_no_auth_for_panel(hassio_client, build_type): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine -def test_forward_request_no_auth_for_logo(hassio_client): +def test_forward_request_no_auth_for_logo(hassio_client, aioclient_mock): """Test no auth needed for .""" - response = MagicMock() - response.read.return_value = mock_coro('data') + aioclient_mock.get( + "http://127.0.0.1/addons/bl_b392/logo", text="response") - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = 'response' - resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') + resp = yield from hassio_client.get('/api/hassio/addons/bl_b392/logo') # Check we got right response assert resp.status == 200 @@ -97,24 +78,18 @@ def test_forward_request_no_auth_for_logo(hassio_client): assert body == 'response' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine -def test_forward_log_request(hassio_client): +def test_forward_log_request(hassio_client, aioclient_mock): """Test fetching normal log path doesn't remove ANSI color escape codes.""" - response = MagicMock() - response.read.return_value = mock_coro('data') - - with patch('homeassistant.components.hassio.HassIOView._command_proxy', - Mock(return_value=mock_coro(response))), \ - patch('homeassistant.components.hassio.http.' - '_create_response') as mresp: - mresp.return_value = '\033[32mresponse\033[0m' - resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ - HTTP_HEADER_HA_AUTH: API_PASSWORD - }) + aioclient_mock.get( + "http://127.0.0.1/beer/logs", text="\033[32mresponse\033[0m") + + resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) # Check we got right response assert resp.status == 200 @@ -122,8 +97,7 @@ def test_forward_log_request(hassio_client): assert body == '\033[32mresponse\033[0m' # Check we forwarded command - assert len(mresp.mock_calls) == 1 - assert mresp.mock_calls[0][1] == (response, 'data') + assert len(aioclient_mock.mock_calls) == 1 @asyncio.coroutine @@ -151,5 +125,5 @@ async def test_forwarding_user_info(hassio_client, hass_admin_user, assert len(aioclient_mock.mock_calls) == 1 req_headers = aioclient_mock.mock_calls[0][-1] - req_headers['X-HASS-USER-ID'] == hass_admin_user.id - req_headers['X-HASS-IS-ADMIN'] == '1' + req_headers['X-Hass-User-ID'] == hass_admin_user.id + req_headers['X-Hass-Is-Admin'] == '1' diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py new file mode 100644 index 00000000000000..4e071ba24fd897 --- /dev/null +++ b/tests/components/hassio/test_ingress.py @@ -0,0 +1,162 @@ +"""The tests for the hassio component.""" + +from aiohttp.hdrs import X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO +from aiohttp.client_exceptions import WSServerHandshakeError +import pytest + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_get( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.get( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_post( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.post("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.post( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_put( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.put("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.put( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ]) +async def test_ingress_request_delete( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.delete("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.delete( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ws"), ("core", "ws.php"), + ("local", "panel/config/stream"), ("jk_921", "hulk") + ]) +async def test_ingress_websocket( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + build_type[0], build_type[1])) + + # Ignore error because we can setup a full IO infrastructure + with pytest.raises(WSServerHandshakeError): + await hassio_client.ws_connect( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index fc4661e7544bd9..f1f148f8495c1f 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -207,7 +207,7 @@ def test_setup_hassio_no_additional_data(hass, aioclient_mock): assert result assert aioclient_mock.call_count == 3 - assert aioclient_mock.mock_calls[-1][3]['X-HASSIO-KEY'] == "123456" + assert aioclient_mock.mock_calls[-1][3]['X-Hassio-Key'] == "123456" @asyncio.coroutine diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index 8b3b057bfc0d84..ab759f03058f1e 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -102,7 +102,7 @@ def create_session(self, loop): async def match_request(self, method, url, *, data=None, auth=None, params=None, headers=None, allow_redirects=None, - timeout=None, json=None): + timeout=None, json=None, cookies=None): """Match a request against pre-registered requests.""" data = data or json url = URL(url) From e0b4e88544df16b13b1eaecee335433d0d1564fd Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Sun, 31 Mar 2019 00:01:58 -0400 Subject: [PATCH 352/605] Update Foscam component to support stream source (#22568) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models --- homeassistant/components/foscam/camera.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index ceec57f77557cc..a11d2f48f625b9 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -8,7 +8,8 @@ import voluptuous as vol -from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA) +from homeassistant.components.camera import ( + Camera, PLATFORM_SCHEMA, SUPPORT_STREAM) from homeassistant.const import ( CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT) from homeassistant.helpers import config_validation as cv @@ -57,6 +58,8 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) + self._media_port = self._foscam_session.get_port_info()[1]['mediaPort'] + def camera_image(self): """Return a still image response from the camera.""" # Send the request to snap a picture and return raw jpg data @@ -67,6 +70,20 @@ def camera_image(self): return response + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + return 'rtsp://{}:{}@{}:{}/videoMain'.format( + self._username, + self._password, + self._foscam_session.host, + self._media_port) + @property def motion_detection_enabled(self): """Camera Motion Detection Status.""" From 56c75d77063ad39bfe926c9a6d4e2ccaabc897a8 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 1 Apr 2019 19:00:25 +0200 Subject: [PATCH 353/605] Update face_recognition to 1.2.3 (#22622) --- homeassistant/components/dlib_face_detect/image_processing.py | 2 +- homeassistant/components/dlib_face_identify/image_processing.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dlib_face_detect/image_processing.py b/homeassistant/components/dlib_face_detect/image_processing.py index cb9ea5ff5f996e..fea756395e427c 100644 --- a/homeassistant/components/dlib_face_detect/image_processing.py +++ b/homeassistant/components/dlib_face_detect/image_processing.py @@ -13,7 +13,7 @@ from homeassistant.components.image_processing import ( ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) -REQUIREMENTS = ['face_recognition==1.0.0'] +REQUIREMENTS = ['face_recognition==1.2.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/dlib_face_identify/image_processing.py b/homeassistant/components/dlib_face_identify/image_processing.py index d8c3f5f621fc80..6611fb0edfbf95 100644 --- a/homeassistant/components/dlib_face_identify/image_processing.py +++ b/homeassistant/components/dlib_face_identify/image_processing.py @@ -15,7 +15,7 @@ CONF_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['face_recognition==1.0.0'] +REQUIREMENTS = ['face_recognition==1.2.3'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 09a447b2a20662..057790cbc6ac55 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -415,7 +415,7 @@ evohomeclient==0.2.8 # homeassistant.components.dlib_face_detect.image_processing # homeassistant.components.dlib_face_identify.image_processing -# face_recognition==1.0.0 +# face_recognition==1.2.3 # homeassistant.components.fastdotcom fastdotcom==0.0.3 From c7576999ca31bd426bd0b5ae2f69059e5efe932c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 10:20:13 -0700 Subject: [PATCH 354/605] Disable Z-Wave autoheal (#22628) --- homeassistant/components/zwave/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zwave/const.py b/homeassistant/components/zwave/const.py index fece48655dfd23..67b5341a4e60a2 100644 --- a/homeassistant/components/zwave/const.py +++ b/homeassistant/components/zwave/const.py @@ -29,7 +29,7 @@ CONF_CONFIG_PATH = 'config_path' CONF_NETWORK_KEY = 'network_key' -DEFAULT_CONF_AUTOHEAL = True +DEFAULT_CONF_AUTOHEAL = False DEFAULT_CONF_USB_STICK_PATH = '/zwaveusbstick' DEFAULT_POLLING_INTERVAL = 60000 DEFAULT_DEBUG = False From a5c7f131eefb98a44a49cd31e1838a19b51cee0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Mon, 1 Apr 2019 19:33:38 +0200 Subject: [PATCH 355/605] Handle disonnect bug in Tibber library (#22629) --- homeassistant/components/tibber/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index 135437801d934b..19cf6fe65258ea 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.10.0'] +REQUIREMENTS = ['pyTibber==0.10.1'] DOMAIN = 'tibber' diff --git a/requirements_all.txt b/requirements_all.txt index 057790cbc6ac55..a773c9c8c31248 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -922,7 +922,7 @@ pyRFXtrx==0.23 # pySwitchmate==0.4.5 # homeassistant.components.tibber -pyTibber==0.10.0 +pyTibber==0.10.1 # homeassistant.components.dlink.switch pyW215==0.6.0 From 6d741d68b74e81be4c72399160aee9d69213bb24 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 2 Apr 2019 02:41:08 +0200 Subject: [PATCH 356/605] Support GET params for websocket ingress path (#22638) --- homeassistant/components/hassio/ingress.py | 5 +++++ tests/components/hassio/test_ingress.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 6c1ef389712dff..04241f53de9c0b 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -71,9 +71,14 @@ async def _handle_websocket( ws_server = web.WebSocketResponse() await ws_server.prepare(request) + # Preparing url = self._create_url(addon, path) source_header = _init_header(request, addon) + # Support GET query + if request.query_string: + url = "{}?{}".format(url, request.query_string) + # Start proxy async with self._websession.ws_connect( url, headers=source_header diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 4e071ba24fd897..4b72eda4c2596a 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -136,7 +136,8 @@ async def test_ingress_request_delete( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ws"), ("core", "ws.php"), - ("local", "panel/config/stream"), ("jk_921", "hulk") + ("local", "panel/config/stream"), ("jk_921", "hulk"), + ("demo", "ws/connection?id=9&token=SJAKWS283") ]) async def test_ingress_websocket( hassio_client, build_type, aioclient_mock): From e3ca1e6203fabc2363e248186e548122fed5c254 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Tue, 2 Apr 2019 12:59:38 -0400 Subject: [PATCH 357/605] Return 0 for failed Foscam streams (#22651) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models * changing logic for success and return none --- homeassistant/components/foscam/camera.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index a11d2f48f625b9..b6f2162d57a9d9 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -58,7 +58,10 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._media_port = self._foscam_session.get_port_info()[1]['mediaPort'] + self._media_port = None + result, response = self._foscam_session.get_port_info() + if result == 0: + self._media_port = response['mediaPort'] def camera_image(self): """Return a still image response from the camera.""" @@ -73,16 +76,20 @@ def camera_image(self): @property def supported_features(self): """Return supported features.""" - return SUPPORT_STREAM + if self._media_port: + return SUPPORT_STREAM + return 0 @property def stream_source(self): """Return the stream source.""" - return 'rtsp://{}:{}@{}:{}/videoMain'.format( - self._username, - self._password, - self._foscam_session.host, - self._media_port) + if self._media_port: + return 'rtsp://{}:{}@{}:{}/videoMain'.format( + self._username, + self._password, + self._foscam_session.host, + self._media_port) + return None @property def motion_detection_enabled(self): From 31ac965b163fb95681a0c26e52533f2db990f3ea Mon Sep 17 00:00:00 2001 From: Jc2k Date: Tue, 2 Apr 2019 08:57:58 +0100 Subject: [PATCH 358/605] Fix racy homekit_controller platform setup caused by #22368 (#22655) --- homeassistant/components/homekit_controller/__init__.py | 3 +-- homeassistant/components/homekit_controller/connection.py | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 44af8bffe26a74..2a43d0ac9ce5c4 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -191,8 +191,7 @@ def discovery_dispatch(service, discovery_info): return _LOGGER.debug('Discovered unique device %s', hkid) - device = HKDevice(hass, host, port, model, hkid, config_num, config) - hass.data[KNOWN_DEVICES][hkid] = device + HKDevice(hass, host, port, model, hkid, config_num, config) hass.data[KNOWN_DEVICES] = {} discovery.listen(hass, SERVICE_HOMEKIT, discovery_dispatch) diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index d875b91eb2c54d..2ca568b547fd3e 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -7,7 +7,8 @@ from homeassistant.helpers.event import call_later from .const import ( - CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, PAIRING_FILE, HOMEKIT_DIR + CONTROLLER, DOMAIN, HOMEKIT_ACCESSORY_DISPATCH, KNOWN_DEVICES, + PAIRING_FILE, HOMEKIT_DIR ) @@ -76,6 +77,8 @@ def __init__(self, hass, host, port, model, hkid, config_num, config): self.pairing = self.controller.pairings.get(hkid) + hass.data[KNOWN_DEVICES][hkid] = self + if self.pairing is not None: self.accessory_setup() else: From 33575962156f6cbff0928b020230258065c0ae91 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 2 Apr 2019 11:42:01 -0700 Subject: [PATCH 359/605] Bumped version to 0.91.0b5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ea4b51e4bd9274..4091bc2ed02e02 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b4' +PATCH_VERSION = '0b5' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 5651db4b5c8fa7efbea4f2ea271c4c4e7dbbf01f Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Tue, 2 Apr 2019 15:22:49 -0500 Subject: [PATCH 360/605] Add discovery support to HEOS component (#22652) * Add discovery entry point * Fix test * Correct test call method * Update netdisco to 2.6.0 --- .../components/discovery/__init__.py | 4 ++- homeassistant/components/heos/config_flow.py | 7 +++++- requirements_all.txt | 2 +- tests/components/heos/test_config_flow.py | 25 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index ecbbe7ea5e0657..8e3a350c5ca1b0 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -20,7 +20,7 @@ from homeassistant.helpers.discovery import async_load_platform, async_discover import homeassistant.util.dt as dt_util -REQUIREMENTS = ['netdisco==2.5.0'] +REQUIREMENTS = ['netdisco==2.6.0'] DOMAIN = 'discovery' @@ -35,6 +35,7 @@ SERVICE_HASS_IOS_APP = 'hass_ios' SERVICE_HASSIO = 'hassio' SERVICE_HOMEKIT = 'homekit' +SERVICE_HEOS = 'heos' SERVICE_HUE = 'philips_hue' SERVICE_IGD = 'igd' SERVICE_IKEA_TRADFRI = 'ikea_tradfri' @@ -57,6 +58,7 @@ SERVICE_DECONZ: 'deconz', 'esphome': 'esphome', 'google_cast': 'cast', + SERVICE_HEOS: 'heos', SERVICE_HUE: 'hue', SERVICE_TELLDUSLIVE: 'tellduslive', SERVICE_IKEA_TRADFRI: 'tradfri', diff --git a/homeassistant/components/heos/config_flow.py b/homeassistant/components/heos/config_flow.py index 5fd7ea59912f15..7ccb43c60e9e62 100644 --- a/homeassistant/components/heos/config_flow.py +++ b/homeassistant/components/heos/config_flow.py @@ -21,6 +21,11 @@ class HeosFlowHandler(config_entries.ConfigFlow): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + async def async_step_discovery(self, discovery_info): + """Handle a discovered Heos device.""" + return await self.async_step_user( + {CONF_HOST: discovery_info[CONF_HOST]}) + async def async_step_import(self, user_input=None): """Occurs when an entry is setup through config.""" host = user_input[CONF_HOST] @@ -32,7 +37,7 @@ async def async_step_user(self, user_input=None): """Obtain host and validate connection.""" from pyheos import Heos - # Only a single entry is supported + # Only a single entry is needed for all devices entries = self.hass.config_entries.async_entries(DOMAIN) if entries: return self.async_abort(reason='already_setup') diff --git a/requirements_all.txt b/requirements_all.txt index 8c85c9edbc5358..a6f0e055c9a09b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -741,7 +741,7 @@ nessclient==0.9.15 netdata==0.1.2 # homeassistant.components.discovery -netdisco==2.5.0 +netdisco==2.6.0 # homeassistant.components.neurio_energy.sensor neurio==0.3.1 diff --git a/tests/components/heos/test_config_flow.py b/tests/components/heos/test_config_flow.py index 8314ad07bc22b1..ddb2bd39384a69 100644 --- a/tests/components/heos/test_config_flow.py +++ b/tests/components/heos/test_config_flow.py @@ -55,3 +55,28 @@ async def test_create_entry_when_host_valid(hass, controller): assert result['data'] == data assert controller.connect.call_count == 1 assert controller.disconnect.call_count == 1 + + +async def test_create_entry_with_discovery(hass, controller): + """Test result type is create entry when valid through discovery.""" + flow = HeosFlowHandler() + flow.hass = hass + data = { + 'host': '127.0.0.1', + 'manufacturer': 'Denon', + 'model_name': 'HEOS Drive', + 'model_number': 'DWSA-10 4.0', + 'name': 'Office', + 'port': 60006, + 'serial': None, + 'ssdp_description': + 'http://127.0.0.1:60006/upnp/desc/aios_device/aios_device.xml', + 'udn': 'uuid:e61de70c-2250-1c22-0080-0005cdf512be', + 'upnp_device_type': 'urn:schemas-denon-com:device:AiosDevice:1' + } + result = await flow.async_step_discovery(data) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'Controller (127.0.0.1)' + assert result['data'] == {'host': '127.0.0.1'} + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 From 22d93a74a41d2d6a6d92fe8f97bbbfc631583669 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Tue, 2 Apr 2019 22:57:38 +0200 Subject: [PATCH 361/605] Don't use room setpoint override in climate.opentherm_gw (#22656) * Dont use DATA_ROOM_SETPOINT_OVRD in climate.opentherm_gw as it is unreliable with some thermostats. * Show new target temperature immediately until the backend notices a change * Only update target temp on the gateway if the value differs from the current target_temperature. --- homeassistant/components/opentherm_gw/climate.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 58ce49a9b02bfb..60f1901d43e96d 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -39,6 +39,7 @@ def __init__(self, hass, config): self.temp_precision = config.get(CONF_PRECISION) self._current_operation = STATE_IDLE self._current_temperature = None + self._new_target_temperature = None self._target_temperature = None self._away_mode_a = None self._away_mode_b = None @@ -63,11 +64,10 @@ async def receive_report(self, status): else: self._current_operation = STATE_IDLE self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP) - - temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT_OVRD) - if temp is None: - temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT) - self._target_temperature = temp + temp_upd = status.get(self._gw_vars.DATA_ROOM_SETPOINT) + if self._target_temperature != temp_upd: + self._new_target_temperature = None + self._target_temperature = temp_upd # GPIO mode 5: 0 == Away # GPIO mode 6: 1 == Away @@ -138,7 +138,7 @@ def current_temperature(self): @property def target_temperature(self): """Return the temperature we try to reach.""" - return self._target_temperature + return self._new_target_temperature or self._target_temperature @property def target_temperature_step(self): @@ -154,7 +154,9 @@ async def async_set_temperature(self, **kwargs): """Set new target temperature.""" if ATTR_TEMPERATURE in kwargs: temp = float(kwargs[ATTR_TEMPERATURE]) - self._target_temperature = await self._gateway.set_target_temp( + if temp == self.target_temperature: + return + self._new_target_temperature = await self._gateway.set_target_temp( temp) self.async_schedule_update_ha_state() From 5613e8bb60b5fff83bc30efcfee5915aab2c6def Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 04:23:33 +0200 Subject: [PATCH 362/605] Hass.io discovery flow deconz (#22623) * Add Hass.io deCONZ discovery flow * add bridge ID * fix attribute * fix strings * Address comments * Add test * Add only instance / changed maybe later --- .../components/deconz/config_flow.py | 59 +++++++++++++++++-- homeassistant/components/deconz/const.py | 3 + homeassistant/components/deconz/strings.json | 10 +++- homeassistant/components/hassio/discovery.py | 4 +- tests/components/deconz/test_config_flow.py | 49 +++++++++++++++ 5 files changed, 116 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index cabb5b46ece5bc..38849fb37b3ff3 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -1,17 +1,18 @@ """Config flow to configure deCONZ component.""" import asyncio + import async_timeout import voluptuous as vol from homeassistant import config_entries -from homeassistant.core import callback from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT +from homeassistant.core import callback from homeassistant.helpers import aiohttp_client from .const import ( - CONF_ALLOW_DECONZ_GROUPS, CONF_ALLOW_CLIP_SENSOR, DEFAULT_PORT, DOMAIN) - -CONF_BRIDGEID = 'bridgeid' + CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, + DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT, + DOMAIN) @callback @@ -28,6 +29,8 @@ class DeconzFlowHandler(config_entries.ConfigFlow): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + _hassio_discovery = None + def __init__(self): """Initialize the deCONZ config flow.""" self.bridges = [] @@ -151,8 +154,10 @@ async def async_step_options(self, user_input=None): return self.async_show_form( step_id='options', data_schema=vol.Schema({ - vol.Optional(CONF_ALLOW_CLIP_SENSOR): bool, - vol.Optional(CONF_ALLOW_DECONZ_GROUPS): bool, + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, }), ) @@ -191,3 +196,45 @@ async def async_step_import(self, import_config): user_input = {CONF_ALLOW_CLIP_SENSOR: True, CONF_ALLOW_DECONZ_GROUPS: True} return await self.async_step_options(user_input=user_input) + + async def async_step_hassio(self, user_input=None): + """Prepare configuration for a Hass.io deCONZ bridge. + + This flow is triggered by the discovery component. + """ + if configured_hosts(self.hass): + return self.async_abort(reason='one_instance_only') + + self._hassio_discovery = user_input + + return await self.async_step_hassio_confirm() + + async def async_step_hassio_confirm(self, user_input=None): + """Confirm a Hass.io discovery.""" + if user_input is not None: + data = self._hassio_discovery + + return self.async_create_entry( + title=data['addon'], data={ + CONF_HOST: data[CONF_HOST], + CONF_PORT: data[CONF_PORT], + CONF_BRIDGEID: data['serial'], + CONF_API_KEY: data[CONF_API_KEY], + CONF_ALLOW_CLIP_SENSOR: + user_input[CONF_ALLOW_CLIP_SENSOR], + CONF_ALLOW_DECONZ_GROUPS: + user_input[CONF_ALLOW_DECONZ_GROUPS], + }) + + return self.async_show_form( + step_id='hassio_confirm', + description_placeholders={ + 'addon': self._hassio_discovery['addon'] + }, + data_schema=vol.Schema({ + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, + }) + ) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index e728430f202aba..b26fddd914755c 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -6,9 +6,12 @@ DOMAIN = 'deconz' DEFAULT_PORT = 80 +DEFAULT_ALLOW_CLIP_SENSOR = False +DEFAULT_ALLOW_DECONZ_GROUPS = False CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' +CONF_BRIDGEID = 'bridgeid' SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'cover', 'light', 'scene', 'sensor', 'switch'] diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 1bf7235713afbb..d0ae34e7c7aa90 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -19,6 +19,14 @@ "allow_clip_sensor": "Allow importing virtual sensors", "allow_deconz_groups": "Allow importing deCONZ groups" } + }, + "hassio_confirm": { + "title": "deCONZ Zigbee gateway via Hass.io add-on", + "description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the hass.io add-on {addon}?", + "data": { + "allow_clip_sensor": "Allow importing virtual sensors", + "allow_deconz_groups": "Allow importing deCONZ groups" + } } }, "error": { @@ -30,4 +38,4 @@ "one_instance_only": "Component only supports one deCONZ instance" } } -} \ No newline at end of file +} diff --git a/homeassistant/components/hassio/discovery.py b/homeassistant/components/hassio/discovery.py index 804247d2407d82..09a98edc14804b 100644 --- a/homeassistant/components/hassio/discovery.py +++ b/homeassistant/components/hassio/discovery.py @@ -81,7 +81,7 @@ async def async_process_new(self, data): service = data[ATTR_SERVICE] config_data = data[ATTR_CONFIG] - # Read addinional Add-on info + # Read additional Add-on info try: addon_info = await self.hassio.get_addon_info(data[ATTR_ADDON]) except HassioAPIError as err: @@ -98,7 +98,7 @@ async def async_process_del(self, data): service = data[ATTR_SERVICE] uuid = data[ATTR_UUID] - # Check if realy deletet / prevent injections + # Check if really deletet / prevent injections try: data = await self.hassio.get_discovery_message(uuid) except HassioAPIError: diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 20c74a8288310d..863e4e93fc5873 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -265,3 +265,52 @@ async def test_options(hass, aioclient_mock): 'allow_clip_sensor': False, 'allow_deconz_groups': False } + + +async def test_hassio_single_instance(hass): + """Test we only allow a single config flow.""" + MockConfigEntry(domain='deconz', data={ + 'host': '1.2.3.4' + }).add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + 'deconz', context={'source': 'hassio'}) + assert result['type'] == 'abort' + assert result['reason'] == 'one_instance_only' + + +async def test_hassio_confirm(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + 'deconz', + data={ + 'addon': 'Mock Addon', + 'host': 'mock-deconz', + 'port': 8080, + 'serial': 'aa:bb', + 'api_key': '1234567890ABCDEF', + }, + context={'source': 'hassio'} + ) + assert result['type'] == 'form' + assert result['step_id'] == 'hassio_confirm' + assert result['description_placeholders'] == { + 'addon': 'Mock Addon', + } + + result = await hass.config_entries.flow.async_configure( + result['flow_id'], { + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + } + ) + + assert result['type'] == 'create_entry' + assert result['result'].data == { + 'host': 'mock-deconz', + 'port': 8080, + 'bridgeid': 'aa:bb', + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + } From 3453d67cfe5bef0e0b68d04d1ffe65c50ea2ca92 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Wed, 3 Apr 2019 04:43:06 +0200 Subject: [PATCH 363/605] Person schema for merge_packages #21307 (#21703) * Person schema for merge_packages #21307 * empty list * skip empty persons * hound * test schema * ensure_none * remove any test changes * remove_falsy validator * nice! * coretests --- homeassistant/components/person/__init__.py | 3 ++- homeassistant/helpers/config_validation.py | 5 +++++ script/inspect_schemas.py | 2 +- tests/helpers/test_config_validation.py | 17 +++++++++++------ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index e6f83b80ba4186..89fac76149786e 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -50,7 +50,8 @@ }) CONFIG_SCHEMA = vol.Schema({ - vol.Optional(DOMAIN): vol.Any(vol.All(cv.ensure_list, [PERSON_SCHEMA]), {}) + vol.Optional(DOMAIN): vol.All( + cv.ensure_list, cv.remove_falsy, [PERSON_SCHEMA]) }, extra=vol.ALLOW_EXTRA) _UNDEF = object() diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 6513f9368b0801..a954d01856ee80 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -349,6 +349,11 @@ def positive_timedelta(value: timedelta) -> timedelta: return value +def remove_falsy(value: Sequence[T]) -> Sequence[T]: + """Remove falsy values from a list.""" + return [v for v in value if v] + + def service(value): """Validate service.""" # Services use same format as entities so we can use same helper. diff --git a/script/inspect_schemas.py b/script/inspect_schemas.py index 46d5cf92eccb41..9904552c681320 100755 --- a/script/inspect_schemas.py +++ b/script/inspect_schemas.py @@ -48,7 +48,7 @@ def add_msg(key, item): schema_type, schema = _identify_config_schema(module) - add_msg("CONFIG_SCHEMA " + schema_type, module_name + ' ' + + add_msg("CONFIG_SCHEMA " + str(schema_type), module_name + ' ' + color('cyan', str(schema)[:60])) for key in sorted(msg): diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 4a883fbf2fd5ec..e0bd509d33045f 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1,15 +1,15 @@ """Test config validators.""" -from datetime import timedelta, datetime, date +from datetime import date, datetime, timedelta import enum import os from socket import _GLOBAL_DEFAULT_TIMEOUT from unittest.mock import Mock, patch import uuid -import homeassistant import pytest import voluptuous as vol +import homeassistant import homeassistant.helpers.config_validation as cv @@ -291,6 +291,11 @@ def test_time_period(): assert -1 * timedelta(hours=1, minutes=15) == schema('-1:15') +def test_remove_falsy(): + """Test remove falsy.""" + assert cv.remove_falsy([0, None, 1, "1", {}, [], ""]) == [1, "1"] + + def test_service(): """Test service validation.""" schema = vol.Schema(cv.service) @@ -908,7 +913,7 @@ def test_matches_regex(): schema(" nrtd ") test_str = "This is a test including uiae." - assert (schema(test_str) == test_str) + assert schema(test_str) == test_str def test_is_regex(): @@ -982,6 +987,6 @@ def test_uuid4_hex(caplog): # the 17th char should be 8-a schema('a03d31b22eee4acc7b90eec40be6ed23') - hex = uuid.uuid4().hex - assert schema(hex) == hex - assert schema(hex.upper()) == hex + _hex = uuid.uuid4().hex + assert schema(_hex) == _hex + assert schema(_hex.upper()) == _hex From 4f2435103bf24bd5b158769811947135d99fbaaf Mon Sep 17 00:00:00 2001 From: emontnemery Date: Wed, 3 Apr 2019 04:58:02 +0200 Subject: [PATCH 364/605] Cast: Fix next/previous track (#22634) * Fix next/previous track * Bump pychromecast * Update test, fixup --- homeassistant/components/cast/__init__.py | 2 +- homeassistant/components/cast/media_player.py | 27 +++++++++++++------ requirements_all.txt | 2 +- tests/components/cast/test_media_player.py | 8 +++--- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/cast/__init__.py b/homeassistant/components/cast/__init__.py index 1aea4655e17ddd..0ec3ac150d7501 100644 --- a/homeassistant/components/cast/__init__.py +++ b/homeassistant/components/cast/__init__.py @@ -2,7 +2,7 @@ from homeassistant import config_entries from homeassistant.helpers import config_entry_flow -REQUIREMENTS = ['pychromecast==3.1.0'] +REQUIREMENTS = ['pychromecast==3.2.0'] DOMAIN = 'cast' diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index cb60cdc296727f..2635a061e605da 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -12,8 +12,8 @@ from homeassistant.components.media_player.const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, - SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, - SUPPORT_VOLUME_SET) + SUPPORT_SEEK, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, + SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) from homeassistant.const import ( CONF_HOST, EVENT_HOMEASSISTANT_STOP, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) @@ -36,9 +36,9 @@ DEFAULT_PORT = 8009 -SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ - SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \ - SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_PLAY +SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_PLAY | SUPPORT_PLAY_MEDIA | \ + SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \ + SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET # Stores a threading.Lock that is held by the internal pychromecast discovery. INTERNAL_DISCOVERY_RUNNING_KEY = 'cast_discovery_running' @@ -931,12 +931,12 @@ def media_stop(self): def media_previous_track(self): """Send previous track command.""" media_controller = self._media_controller() - media_controller.rewind() + media_controller.queue_prev() def media_next_track(self): """Send next track command.""" media_controller = self._media_controller() - media_controller.skip() + media_controller.queue_next() def media_seek(self, position): """Seek the media to a specific location.""" @@ -1130,7 +1130,18 @@ def app_name(self): @property def supported_features(self): """Flag media player features that are supported.""" - return SUPPORT_CAST + support = SUPPORT_CAST + media_status, _ = self._media_status() + + if media_status: + if media_status.supports_queue_next: + support |= SUPPORT_PREVIOUS_TRACK + if media_status.supports_queue_next: + support |= SUPPORT_NEXT_TRACK + if media_status.supports_seek: + support |= SUPPORT_SEEK + + return support @property def media_position(self): diff --git a/requirements_all.txt b/requirements_all.txt index a6f0e055c9a09b..6d136a11fb1101 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -980,7 +980,7 @@ pycfdns==0.0.1 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==3.1.0 +pychromecast==3.2.0 # homeassistant.components.cmus.media_player pycmus==0.1.1 diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index 7c40b09d03ecf8..e7418460c595d6 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -479,16 +479,16 @@ async def test_dynamic_group_media_control(hass: HomeAssistantType): group_media_status.player_is_playing = True entity.new_dynamic_group_media_status(group_media_status) entity.media_previous_track() - assert entity._dynamic_group_cast.media_controller.rewind.called - assert not chromecast.media_controller.rewind.called + assert entity._dynamic_group_cast.media_controller.queue_prev.called + assert not chromecast.media_controller.queue_prev.called # Player is paused, dynamic group is playing -> Should not forward player_media_status.player_is_playing = False player_media_status.player_is_paused = True entity.new_media_status(player_media_status) entity.media_next_track() - assert not entity._dynamic_group_cast.media_controller.skip.called - assert chromecast.media_controller.skip.called + assert not entity._dynamic_group_cast.media_controller.queue_next.called + assert chromecast.media_controller.queue_next.called # Player is in unknown state, dynamic group is playing -> Should forward player_media_status.player_state = "UNKNOWN" From e736521e9f8971d9e54d401ab9e031a75a5dd930 Mon Sep 17 00:00:00 2001 From: emontnemery Date: Wed, 3 Apr 2019 04:58:28 +0200 Subject: [PATCH 365/605] Fix regression from PR #22396 (#22661) * Fix regression from PR #22396 * Fix test --- homeassistant/components/cast/media_player.py | 3 ++- tests/components/cast/test_media_player.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 2635a061e605da..c4019b4686c7be 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -986,7 +986,8 @@ def _media_status(self): media_status = self.media_status media_status_received = self.media_status_received - if media_status is None or media_status.player_state == "UNKNOWN": + if ((media_status is None or media_status.player_state == "UNKNOWN") + and self._dynamic_group_cast is not None): media_status = self.dynamic_group_media_status media_status_received = self.dynamic_group_media_status_received diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index e7418460c595d6..78140d49e4a2dc 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -373,6 +373,7 @@ async def test_dynamic_group_media_states(hass: HomeAssistantType): player_media_status = MagicMock(images=None) # Player has no state, dynamic group is playing -> Should report 'playing' + entity._dynamic_group_cast = MagicMock() group_media_status.player_is_playing = True entity.new_dynamic_group_media_status(group_media_status) await hass.async_block_till_done() From f2941522cad07effad6a1a793c4fd1c29044b039 Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Wed, 3 Apr 2019 05:35:33 +0200 Subject: [PATCH 366/605] Person tests - split from #21703 (#22663) --- tests/components/person/test_init.py | 43 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tests/components/person/test_init.py b/tests/components/person/test_init.py index ef129a555beee7..cde7633b1a3890 100644 --- a/tests/components/person/test_init.py +++ b/tests/components/person/test_init.py @@ -1,24 +1,26 @@ """The tests for the person component.""" from unittest.mock import Mock +import pytest + +from homeassistant.components.device_tracker import ( + ATTR_SOURCE_TYPE, SOURCE_TYPE_GPS, SOURCE_TYPE_ROUTER) from homeassistant.components.person import ( ATTR_SOURCE, ATTR_USER_ID, DOMAIN, PersonManager) from homeassistant.const import ( - ATTR_ID, ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_GPS_ACCURACY, - STATE_UNKNOWN, EVENT_HOMEASSISTANT_START) -from homeassistant.components.device_tracker import ( - ATTR_SOURCE_TYPE, SOURCE_TYPE_GPS, SOURCE_TYPE_ROUTER) + ATTR_GPS_ACCURACY, ATTR_ID, ATTR_LATITUDE, ATTR_LONGITUDE, + EVENT_HOMEASSISTANT_START, STATE_UNKNOWN) from homeassistant.core import CoreState, State from homeassistant.setup import async_setup_component -import pytest - -from tests.common import mock_component, mock_restore_cache, mock_coro_func +from tests.common import ( + assert_setup_component, mock_component, mock_coro_func, mock_restore_cache) DEVICE_TRACKER = 'device_tracker.test_tracker' DEVICE_TRACKER_2 = 'device_tracker.test_tracker_2' +# pylint: disable=redefined-outer-name @pytest.fixture def storage_setup(hass, hass_storage, hass_admin_user): """Storage setup.""" @@ -44,7 +46,8 @@ def storage_setup(hass, hass_storage, hass_admin_user): async def test_minimal_setup(hass): """Test minimal config with only name.""" config = {DOMAIN: {'id': '1234', 'name': 'test person'}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.test_person') assert state.state == STATE_UNKNOWN @@ -71,7 +74,8 @@ async def test_setup_user_id(hass, hass_admin_user): user_id = hass_admin_user.id config = { DOMAIN: {'id': '1234', 'name': 'test person', 'user_id': user_id}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.test_person') assert state.state == STATE_UNKNOWN @@ -88,7 +92,8 @@ async def test_valid_invalid_user_ids(hass, hass_admin_user): config = {DOMAIN: [ {'id': '1234', 'name': 'test valid user', 'user_id': user_id}, {'id': '5678', 'name': 'test bad user', 'user_id': 'bad_user_id'}]} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(2): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.test_valid_user') assert state.state == STATE_UNKNOWN @@ -108,7 +113,8 @@ async def test_setup_tracker(hass, hass_admin_user): config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, 'device_trackers': DEVICE_TRACKER}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.tracked_person') assert state.state == STATE_UNKNOWN @@ -159,7 +165,8 @@ async def test_setup_two_trackers(hass, hass_admin_user): config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, 'device_trackers': [DEVICE_TRACKER, DEVICE_TRACKER_2]}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.tracked_person') assert state.state == STATE_UNKNOWN @@ -231,7 +238,8 @@ async def test_ignore_unavailable_states(hass, hass_admin_user): config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, 'device_trackers': [DEVICE_TRACKER, DEVICE_TRACKER_2]}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.tracked_person') assert state.state == STATE_UNKNOWN @@ -275,7 +283,8 @@ async def test_restore_home_state(hass, hass_admin_user): config = {DOMAIN: { 'id': '1234', 'name': 'tracked person', 'user_id': user_id, 'device_trackers': DEVICE_TRACKER}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(1): + assert await async_setup_component(hass, DOMAIN, config) state = hass.states.get('person.tracked_person') assert state.state == 'home' @@ -292,7 +301,8 @@ async def test_duplicate_ids(hass, hass_admin_user): config = {DOMAIN: [ {'id': '1234', 'name': 'test user 1'}, {'id': '1234', 'name': 'test user 2'}]} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(2): + assert await async_setup_component(hass, DOMAIN, config) assert len(hass.states.async_entity_ids('person')) == 1 assert hass.states.get('person.test_user_1') is not None @@ -302,7 +312,8 @@ async def test_duplicate_ids(hass, hass_admin_user): async def test_create_person_during_run(hass): """Test that person is updated if created while hass is running.""" config = {DOMAIN: {}} - assert await async_setup_component(hass, DOMAIN, config) + with assert_setup_component(0): + assert await async_setup_component(hass, DOMAIN, config) hass.states.async_set(DEVICE_TRACKER, 'home') await hass.async_block_till_done() From 6a411710dfe84094182146e973a194a89ef931fb Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 2 Apr 2019 21:23:59 -0700 Subject: [PATCH 367/605] Fix trusted networks auth provider warning message (#22671) * Fix trusted networks auth provider warning message * Update auth.py --- homeassistant/components/http/auth.py | 16 ++++++++++------ homeassistant/components/http/view.py | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 7b8508894ce73d..0d8e327e086e19 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,12 +190,16 @@ async def auth_middleware(request, handler): elif (trusted_networks and await async_validate_trusted_networks(request)): - _LOGGER.warning( - 'Access from trusted networks without auth token is going to ' - 'be removed in Home Assistant 0.96. Configure the trusted ' - 'networks auth provider or use long-lived access tokens to ' - 'access %s from %s', - request.path, request[KEY_REAL_IP]) + if request.path not in old_auth_warning: + # When removing this, don't forget to remove the print logic + # in http/view.py + request['deprecate_warning_message'] = \ + 'Access from trusted networks without auth token is ' \ + 'going to be removed in Home Assistant 0.96. Configure ' \ + 'the trusted networks auth provider or use long-lived ' \ + 'access tokens to access {} from {}'.format( + request.path, request[KEY_REAL_IP]) + old_auth_warning.add(request.path) authenticated = True elif (support_legacy and HTTP_HEADER_HA_AUTH in request.headers and diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index d68cabebacf39f..8d5e0ee88b136a 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -98,6 +98,8 @@ async def handle(request): if view.requires_auth: if authenticated: + if 'deprecate_warning_message' in request: + _LOGGER.warning(request['deprecate_warning_message']) await process_success_login(request) else: raise HTTPUnauthorized() From a7d49e40c0cc62b5eb81e02dcd1d0b80075c1842 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Wed, 3 Apr 2019 07:25:02 +0100 Subject: [PATCH 368/605] Rebrand Cisco Spark notify to be Cisco Webex Teams (#21938) * Rebrand Cisco Spark notify to be Cisco Webex Teams * Remove property from class * Switch to use html for api * Update notify.py * Rename CONF_ROOMID to CONF_ROOM_ID * updated * Fix lint errors * Update notify.py * Update notify.py * Also validate room ID * Update notify.py * Update .coveragerc * Update notify.py --- .coveragerc | 1 + .../components/cisco_webex_teams/__init__.py | 1 + .../components/cisco_webex_teams/notify.py | 60 +++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 65 insertions(+) create mode 100644 homeassistant/components/cisco_webex_teams/__init__.py create mode 100644 homeassistant/components/cisco_webex_teams/notify.py diff --git a/.coveragerc b/.coveragerc index cae3bf423a95d4..25e5cf8bb037a5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -87,6 +87,7 @@ omit = homeassistant/components/channels/media_player.py homeassistant/components/cisco_ios/device_tracker.py homeassistant/components/cisco_mobility_express/device_tracker.py + homeassistant/components/cisco_webex_teams/notify.py homeassistant/components/ciscospark/notify.py homeassistant/components/citybikes/sensor.py homeassistant/components/clementine/media_player.py diff --git a/homeassistant/components/cisco_webex_teams/__init__.py b/homeassistant/components/cisco_webex_teams/__init__.py new file mode 100644 index 00000000000000..0a8714806a18f3 --- /dev/null +++ b/homeassistant/components/cisco_webex_teams/__init__.py @@ -0,0 +1 @@ +"""Component to integrate the Cisco Webex Teams cloud.""" diff --git a/homeassistant/components/cisco_webex_teams/notify.py b/homeassistant/components/cisco_webex_teams/notify.py new file mode 100644 index 00000000000000..f893d4071b0e6e --- /dev/null +++ b/homeassistant/components/cisco_webex_teams/notify.py @@ -0,0 +1,60 @@ +"""Cisco Webex Teams notify component.""" +import logging + +import voluptuous as vol + +from homeassistant.components.notify import ( + PLATFORM_SCHEMA, BaseNotificationService, ATTR_TITLE) +from homeassistant.const import (CONF_TOKEN) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['webexteamssdk==1.1.1'] + +_LOGGER = logging.getLogger(__name__) + +CONF_ROOM_ID = 'room_id' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_TOKEN): cv.string, + vol.Required(CONF_ROOM_ID): cv.string, +}) + + +def get_service(hass, config, discovery_info=None): + """Get the CiscoWebexTeams notification service.""" + from webexteamssdk import WebexTeamsAPI, exceptions + client = WebexTeamsAPI(access_token=config[CONF_TOKEN]) + try: + # Validate the token & room_id + client.rooms.get(config[CONF_ROOM_ID]) + except exceptions.ApiError as error: + _LOGGER.error(error) + return None + + return CiscoWebexTeamsNotificationService( + client, + config[CONF_ROOM_ID]) + + +class CiscoWebexTeamsNotificationService(BaseNotificationService): + """The Cisco Webex Teams Notification Service.""" + + def __init__(self, client, room): + """Initialize the service.""" + self.room = room + self.client = client + + def send_message(self, message="", **kwargs): + """Send a message to a user.""" + from webexteamssdk import ApiError + title = "" + if kwargs.get(ATTR_TITLE) is not None: + title = "{}{}".format(kwargs.get(ATTR_TITLE), "
") + + try: + self.client.messages.create(roomId=self.room, + html="{}{}".format(title, message)) + except ApiError as api_error: + _LOGGER.error("Could not send CiscoWebexTeams notification. " + "Error: %s", + api_error) diff --git a/requirements_all.txt b/requirements_all.txt index 6d136a11fb1101..8ed77fdfb3e89a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1783,6 +1783,9 @@ watchdog==0.8.3 # homeassistant.components.waterfurnace waterfurnace==1.1.0 +# homeassistant.components.cisco_webex_teams.notify +webexteamssdk==1.1.1 + # homeassistant.components.gpmdp.media_player websocket-client==0.54.0 From 7c5846aed290e405a31d0861a8842d0944280e84 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Wed, 3 Apr 2019 07:49:53 +0100 Subject: [PATCH 369/605] Fix #22648 - Utility_meter would try to cancel a non existing task (#22669) * don't cancel tariff that are paused * test tariffs --- .../components/utility_meter/sensor.py | 3 +- tests/components/utility_meter/test_sensor.py | 41 +++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index dd1514f5e4351b..2c151634a95adf 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -118,7 +118,8 @@ def async_tariff_change(self, entity, old_state, new_state): self._collecting = async_track_state_change( self.hass, self._sensor_source_id, self.async_reading) else: - self._collecting() + if self._collecting: + self._collecting() self._collecting = None _LOGGER.debug("%s - %s - source <%s>", self._name, diff --git a/tests/components/utility_meter/test_sensor.py b/tests/components/utility_meter/test_sensor.py index ee291439a2c877..6b8705bf776ba3 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -6,10 +6,11 @@ from contextlib import contextmanager from tests.common import async_fire_time_changed -from homeassistant.const import EVENT_HOMEASSISTANT_START +from homeassistant.const import EVENT_HOMEASSISTANT_START, ATTR_ENTITY_ID from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from homeassistant.components.utility_meter.const import DOMAIN +from homeassistant.components.utility_meter.const import ( + DOMAIN, SERVICE_SELECT_TARIFF, ATTR_TARIFF) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN _LOGGER = logging.getLogger(__name__) @@ -31,7 +32,7 @@ async def test_state(hass): 'utility_meter': { 'energy_bill': { 'source': 'sensor.energy', - } + 'tariffs': ['onpeak', 'midpeak', 'offpeak']}, } } @@ -51,11 +52,43 @@ async def test_state(hass): force_update=True) await hass.async_block_till_done() - state = hass.states.get('sensor.energy_bill') + state = hass.states.get('sensor.energy_bill_onpeak') + assert state is not None + assert state.state == '1' + + state = hass.states.get('sensor.energy_bill_midpeak') + assert state is not None + assert state.state == '0' + + state = hass.states.get('sensor.energy_bill_offpeak') assert state is not None + assert state.state == '0' + + await hass.services.async_call(DOMAIN, SERVICE_SELECT_TARIFF, { + ATTR_ENTITY_ID: 'utility_meter.energy_bill', ATTR_TARIFF: 'offpeak' + }, blocking=True) + + await hass.async_block_till_done() + now = dt_util.utcnow() + timedelta(seconds=20) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.states.async_set(entity_id, 6, {"unit_of_measurement": "kWh"}, + force_update=True) + await hass.async_block_till_done() + + state = hass.states.get('sensor.energy_bill_onpeak') + assert state is not None assert state.state == '1' + state = hass.states.get('sensor.energy_bill_midpeak') + assert state is not None + assert state.state == '0' + + state = hass.states.get('sensor.energy_bill_offpeak') + assert state is not None + assert state.state == '3' + async def test_net_consumption(hass): """Test utility sensor state.""" From 58a89640bb3c6f65ad2c7f0c906c2c6c6fb8da7f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 12:20:05 +0200 Subject: [PATCH 370/605] Update uvloop to 0.12.2 (#22681) --- virtualization/Docker/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtualization/Docker/Dockerfile.dev b/virtualization/Docker/Dockerfile.dev index 90c9eee3485730..4be2c382226ffd 100644 --- a/virtualization/Docker/Dockerfile.dev +++ b/virtualization/Docker/Dockerfile.dev @@ -29,7 +29,7 @@ COPY requirements_all.txt requirements_all.txt # Uninstall enum34 because some dependencies install it but breaks Python 3.4+. # See PR #8103 for more info. RUN pip3 install --no-cache-dir -r requirements_all.txt && \ - pip3 install --no-cache-dir mysqlclient psycopg2 uvloop==0.11.3 cchardet cython + pip3 install --no-cache-dir mysqlclient psycopg2 uvloop==0.12.2 cchardet cython # BEGIN: Development additions From 7066fb0d101e075e478736f8476fa845a5011b4c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 13:46:41 +0200 Subject: [PATCH 371/605] Fix ffmpeg default extra options (#22682) --- homeassistant/components/amcrest/__init__.py | 4 +++- homeassistant/components/arlo/camera.py | 7 ++++--- homeassistant/components/canary/camera.py | 7 ++++--- homeassistant/components/ffmpeg/camera.py | 4 +++- homeassistant/components/onvif/camera.py | 2 +- homeassistant/components/xiaomi/camera.py | 3 ++- homeassistant/components/yi/camera.py | 3 ++- 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index 295b798c3b1967..ff34ca6f5c7553 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -26,6 +26,7 @@ DEFAULT_PORT = 80 DEFAULT_RESOLUTION = 'high' DEFAULT_STREAM_SOURCE = 'snapshot' +DEFAULT_ARGUMENTS = '-pred 1' TIMEOUT = 10 DATA_AMCREST = 'amcrest' @@ -77,7 +78,8 @@ vol.All(vol.In(RESOLUTION_LIST)), vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE): vol.All(vol.In(STREAM_SOURCE_LIST)), - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): + cv.string, vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): cv.time_period, vol.Optional(CONF_SENSORS): diff --git a/homeassistant/components/arlo/camera.py b/homeassistant/components/arlo/camera.py index 95d11318bf7cbf..d4b00f0062503a 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -13,6 +13,8 @@ from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO +DEPENDENCIES = ['arlo', 'ffmpeg'] + _LOGGER = logging.getLogger(__name__) ARLO_MODE_ARMED = 'armed' @@ -28,8 +30,7 @@ ATTR_LAST_REFRESH = 'last_refresh' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' - -DEPENDENCIES = ['arlo', 'ffmpeg'] +DEFAULT_ARGUMENTS = '-pred 1' POWERSAVE_MODE_MAPPING = { 1: 'best_battery_life', @@ -38,7 +39,7 @@ } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, }) diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index 63c27d31d9339c..c3a3af32450e38 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -18,16 +18,17 @@ from . import DATA_CANARY, DEFAULT_TIMEOUT -CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' - DEPENDENCIES = ['canary', 'ffmpeg'] _LOGGER = logging.getLogger(__name__) +CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' +DEFAULT_ARGUMENTS = '-pred 1' + MIN_TIME_BETWEEN_SESSION_RENEW = timedelta(seconds=90) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, }) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index d897293124bee6..07e56cfd51fdf7 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -20,11 +20,13 @@ _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ffmpeg'] + DEFAULT_NAME = 'FFmpeg' +DEFAULT_ARGUMENTS = "-pred 1" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_INPUT): cv.string, - vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string, + vol.Optional(CONF_EXTRA_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 09d47c3c0c9761..a196f87cd10f2e 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -33,7 +33,7 @@ DEFAULT_PORT = 5000 DEFAULT_USERNAME = 'admin' DEFAULT_PASSWORD = '888888' -DEFAULT_ARGUMENTS = '-q:v 2' +DEFAULT_ARGUMENTS = '-pred 1' DEFAULT_PROFILE = 0 CONF_PROFILE = "profile" diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index d8cd59129abb59..b0acf50ec8cd46 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -23,6 +23,7 @@ DEFAULT_PATH = '/media/mmcblk0p1/record' DEFAULT_PORT = 21 DEFAULT_USERNAME = 'root' +DEFAULT_ARGUMENTS = '-pred 1' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' CONF_MODEL = 'model' @@ -39,7 +40,7 @@ vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string }) diff --git a/homeassistant/components/yi/camera.py b/homeassistant/components/yi/camera.py index c60d4971fb843c..f82c8c38129cb7 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -26,6 +26,7 @@ DEFAULT_PATH = '/tmp/sd/record' DEFAULT_PORT = 21 DEFAULT_USERNAME = 'root' +DEFAULT_ARGUMENTS = '-pred 1' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' @@ -36,7 +37,7 @@ vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string }) From b1cca25299b247e43ddde1e5fd4c376130547850 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 04:53:44 -0700 Subject: [PATCH 372/605] Deal with cover assumed state (#22673) * Deal with cover assumed state * Add docs --- .../components/google_assistant/trait.py | 17 ++++++++++---- .../components/google_assistant/test_trait.py | 23 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 81918ff2e886ad..de3a9530b5035a 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -27,6 +27,7 @@ TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + ATTR_ASSUMED_STATE, ) from homeassistant.core import DOMAIN as HA_DOMAIN from homeassistant.util import color as color_util, temperature as temp_util @@ -1055,11 +1056,19 @@ def query_attributes(self): response = {} if domain == cover.DOMAIN: - position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) - if position is not None: - response['openPercent'] = position + # When it's an assumed state, we will always report it as 50% + # Google will not issue an open command if the assumed state is + # open, even if that is currently incorrect. + if self.state.attributes.get(ATTR_ASSUMED_STATE): + response['openPercent'] = 50 else: - if self.state.state != cover.STATE_CLOSED: + position = self.state.attributes.get( + cover.ATTR_CURRENT_POSITION + ) + + if position is not None: + response['openPercent'] = position + elif self.state.state != cover.STATE_CLOSED: response['openPercent'] = 100 else: response['openPercent'] = 0 diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index a0a710d3d8ce27..81a7fbe1bf7a4f 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -21,7 +21,8 @@ from homeassistant.components.google_assistant import trait, helpers, const from homeassistant.const import ( STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, - TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE) + TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + ATTR_ASSUMED_STATE) from homeassistant.core import State, DOMAIN as HA_DOMAIN, EVENT_CALL_SERVICE from homeassistant.util import color from tests.common import async_mock_service, mock_coro @@ -1059,12 +1060,30 @@ async def test_openclose_cover(hass): assert trait.OpenCloseTrait.supported(cover.DOMAIN, cover.SUPPORT_SET_POSITION) + # No position trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { - cover.ATTR_CURRENT_POSITION: 75 }), BASIC_CONFIG) assert trt.sync_attributes() == {} + assert trt.query_attributes() == { + 'openPercent': 100 + } + + # Assumed state + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + ATTR_ASSUMED_STATE: True, + }), BASIC_CONFIG) + assert trt.sync_attributes() == {} + assert trt.query_attributes() == { + 'openPercent': 50 + } + + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + cover.ATTR_CURRENT_POSITION: 75 + }), BASIC_CONFIG) + + assert trt.sync_attributes() == {} assert trt.query_attributes() == { 'openPercent': 75 } From b797b1513a54a9a6621179de480e60d4932a34f1 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 3 Apr 2019 05:21:25 -0700 Subject: [PATCH 373/605] Add mobile_app notify platform (#22580) * Add mobile_app notify platform * Requested changes * Fix incorrect param for status code * Move push_registrations to notify platform file * Trim down registration information sent in push * quotes * Use async version of load_platform * Add warning for duplicate device names * Switch to async_get_service * add mobile_app.notify test * Update tests/components/mobile_app/test_notify.py * Update tests/components/mobile_app/test_notify.py --- .../components/mobile_app/__init__.py | 13 +- homeassistant/components/mobile_app/const.py | 2 + homeassistant/components/mobile_app/notify.py | 134 ++++++++++++++++++ tests/components/mobile_app/test_notify.py | 81 +++++++++++ 4 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/mobile_app/notify.py create mode 100644 tests/components/mobile_app/test_notify.py diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index ecbe8d708476e7..a4ae78959cf367 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -2,13 +2,13 @@ from homeassistant import config_entries from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.components.webhook import async_register as webhook_register -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, discovery from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, - ATTR_MODEL, ATTR_OS_VERSION, DATA_BINARY_SENSOR, - DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_DEVICES, - DATA_SENSOR, DATA_STORE, DOMAIN, STORAGE_KEY, +from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, + ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, + DATA_BINARY_SENSOR, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, + DATA_DEVICES, DATA_SENSOR, DATA_STORE, DOMAIN, STORAGE_KEY, STORAGE_VERSION) from .http_api import RegistrationsView @@ -52,6 +52,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): except ValueError: pass + hass.async_create_task(discovery.async_load_platform( + hass, 'notify', DOMAIN, {}, config)) + return True diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 38897056c11289..b59c631ba9936d 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -40,6 +40,8 @@ ATTR_MODEL = 'model' ATTR_OS_NAME = 'os_name' ATTR_OS_VERSION = 'os_version' +ATTR_PUSH_TOKEN = 'push_token' +ATTR_PUSH_URL = 'push_url' ATTR_SUPPORTS_ENCRYPTION = 'supports_encryption' ATTR_EVENT_DATA = 'event_data' diff --git a/homeassistant/components/mobile_app/notify.py b/homeassistant/components/mobile_app/notify.py new file mode 100644 index 00000000000000..0120b1a6ffbac2 --- /dev/null +++ b/homeassistant/components/mobile_app/notify.py @@ -0,0 +1,134 @@ +"""Support for mobile_app push notifications.""" +import asyncio +from datetime import datetime, timezone +import logging + +import async_timeout + +from homeassistant.components.notify import ( + ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, + BaseNotificationService) +from homeassistant.components.mobile_app.const import ( + ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, ATTR_DEVICE_NAME, + ATTR_OS_VERSION, ATTR_PUSH_TOKEN, ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, + DOMAIN) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.util.dt as dt_util + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['mobile_app'] + + +def push_registrations(hass): + """Return a dictionary of push enabled registrations.""" + targets = {} + for webhook_id, entry in hass.data[DOMAIN][DATA_CONFIG_ENTRIES].items(): + data = entry.data + app_data = data[ATTR_APP_DATA] + if ATTR_PUSH_TOKEN in app_data and ATTR_PUSH_URL in app_data: + device_name = data[ATTR_DEVICE_NAME] + if device_name in targets: + _LOGGER.warning("Found duplicate device name %s", device_name) + continue + targets[device_name] = webhook_id + return targets + + +# pylint: disable=invalid-name +def log_rate_limits(hass, device_name, resp, level=logging.INFO): + """Output rate limit log line at given level.""" + rate_limits = resp['rateLimits'] + resetsAt = dt_util.parse_datetime(rate_limits['resetsAt']) + resetsAtTime = resetsAt - datetime.now(timezone.utc) + rate_limit_msg = ("mobile_app push notification rate limits for %s: " + "%d sent, %d allowed, %d errors, " + "resets in %s") + _LOGGER.log(level, rate_limit_msg, + device_name, + rate_limits['successful'], + rate_limits['maximum'], rate_limits['errors'], + str(resetsAtTime).split(".")[0]) + + +async def async_get_service(hass, config, discovery_info=None): + """Get the mobile_app notification service.""" + session = async_get_clientsession(hass) + return MobileAppNotificationService(session) + + +class MobileAppNotificationService(BaseNotificationService): + """Implement the notification service for mobile_app.""" + + def __init__(self, session): + """Initialize the service.""" + self._session = session + + @property + def targets(self): + """Return a dictionary of registered targets.""" + return push_registrations(self.hass) + + async def async_send_message(self, message="", **kwargs): + """Send a message to the Lambda APNS gateway.""" + data = {ATTR_MESSAGE: message} + + if kwargs.get(ATTR_TITLE) is not None: + # Remove default title from notifications. + if kwargs.get(ATTR_TITLE) != ATTR_TITLE_DEFAULT: + data[ATTR_TITLE] = kwargs.get(ATTR_TITLE) + + targets = kwargs.get(ATTR_TARGET) + + if not targets: + targets = push_registrations(self.hass) + + if kwargs.get(ATTR_DATA) is not None: + data[ATTR_DATA] = kwargs.get(ATTR_DATA) + + for target in targets: + + entry = self.hass.data[DOMAIN][DATA_CONFIG_ENTRIES][target] + entry_data = entry.data + + app_data = entry_data[ATTR_APP_DATA] + push_token = app_data[ATTR_PUSH_TOKEN] + push_url = app_data[ATTR_PUSH_URL] + + data[ATTR_PUSH_TOKEN] = push_token + + reg_info = { + ATTR_APP_ID: entry_data[ATTR_APP_ID], + ATTR_APP_VERSION: entry_data[ATTR_APP_VERSION], + } + if ATTR_OS_VERSION in entry_data: + reg_info[ATTR_OS_VERSION] = entry_data[ATTR_OS_VERSION] + + data['registration_info'] = reg_info + + try: + with async_timeout.timeout(10, loop=self.hass.loop): + response = await self._session.post(push_url, json=data) + result = await response.json() + + if response.status == 201: + log_rate_limits(self.hass, + entry_data[ATTR_DEVICE_NAME], result) + return + + fallback_error = result.get("errorMessage", + "Unknown error") + fallback_message = ("Internal server error, " + "please try again later: " + "{}").format(fallback_error) + message = result.get("message", fallback_message) + if response.status == 429: + _LOGGER.warning(message) + log_rate_limits(self.hass, + entry_data[ATTR_DEVICE_NAME], + result, logging.WARNING) + else: + _LOGGER.error(message) + + except asyncio.TimeoutError: + _LOGGER.error("Timeout sending notification to %s", push_url) diff --git a/tests/components/mobile_app/test_notify.py b/tests/components/mobile_app/test_notify.py new file mode 100644 index 00000000000000..395dee6c11700f --- /dev/null +++ b/tests/components/mobile_app/test_notify.py @@ -0,0 +1,81 @@ +"""Notify platform tests for mobile_app.""" +# pylint: disable=redefined-outer-name +import pytest + +from homeassistant.setup import async_setup_component + +from homeassistant.components.mobile_app.const import DOMAIN + +from tests.common import MockConfigEntry + + +@pytest.fixture +async def setup_push_receiver(hass, aioclient_mock): + """Fixture that sets up a mocked push receiver.""" + push_url = 'https://mobile-push.home-assistant.dev/push' + + from datetime import datetime, timedelta + now = (datetime.now() + timedelta(hours=24)) + iso_time = now.strftime("%Y-%m-%dT%H:%M:%SZ") + + aioclient_mock.post(push_url, json={ + 'rateLimits': { + 'attempts': 1, + 'successful': 1, + 'errors': 0, + 'total': 1, + 'maximum': 150, + 'remaining': 149, + 'resetsAt': iso_time + } + }) + + entry = MockConfigEntry( + connection_class="cloud_push", + data={ + "app_data": { + "push_token": "PUSH_TOKEN", + "push_url": push_url + }, + "app_id": "io.homeassistant.mobile_app", + "app_name": "mobile_app tests", + "app_version": "1.0", + "device_id": "4d5e6f", + "device_name": "Test", + "manufacturer": "Home Assistant", + "model": "mobile_app", + "os_name": "Linux", + "os_version": "5.0.6", + "secret": "123abc", + "supports_encryption": False, + "user_id": "1a2b3c", + "webhook_id": "webhook_id" + }, + domain=DOMAIN, + source="registration", + title="mobile_app test entry", + version=1 + ) + entry.add_to_hass(hass) + + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + + +async def test_notify_works(hass, aioclient_mock, setup_push_receiver): + """Test notify works.""" + assert hass.services.has_service('notify', 'mobile_app_test') is True + assert await hass.services.async_call('notify', 'mobile_app_test', + {'message': 'Hello world'}, + blocking=True) + + assert len(aioclient_mock.mock_calls) == 1 + call = aioclient_mock.mock_calls + + call_json = call[0][2] + + assert call_json["push_token"] == "PUSH_TOKEN" + assert call_json["message"] == "Hello world" + assert call_json["registration_info"]["app_id"] == \ + "io.homeassistant.mobile_app" + assert call_json["registration_info"]["app_version"] == "1.0" From 625c8e0ceeca0eff0513058d3ad784f01eda5644 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Wed, 3 Apr 2019 09:40:48 -0400 Subject: [PATCH 374/605] Shutdown ZHAGateway on hass closing. (#22646) * Shutdown ZHAGateway on hass stop. * Cleanup ZHA event leftovers. --- homeassistant/components/zha/__init__.py | 21 ++++++++------------ homeassistant/components/zha/core/const.py | 1 - homeassistant/components/zha/core/gateway.py | 16 +++++++++------ homeassistant/components/zha/core/helpers.py | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 292b4fde61f9ac..088ffff13d1741 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -16,15 +16,14 @@ from . import config_flow # noqa # pylint: disable=unused-import from . import api from .core import ZHAGateway +from .core.channels.registry import populate_channel_registry from .core.const import ( COMPONENTS, CONF_BAUDRATE, CONF_DATABASE, CONF_DEVICE_CONFIG, - CONF_RADIO_TYPE, CONF_USB_PATH, DATA_ZHA, - DATA_ZHA_CONFIG, DATA_ZHA_CORE_COMPONENT, DATA_ZHA_DISPATCHERS, - DATA_ZHA_RADIO, DEFAULT_BAUDRATE, DATA_ZHA_GATEWAY, - DEFAULT_RADIO_TYPE, DOMAIN, RadioType, DATA_ZHA_CORE_EVENTS, ENABLE_QUIRKS) -from .core.registries import establish_device_mappings -from .core.channels.registry import populate_channel_registry + CONF_RADIO_TYPE, CONF_USB_PATH, DATA_ZHA, DATA_ZHA_CONFIG, + DATA_ZHA_CORE_COMPONENT, DATA_ZHA_DISPATCHERS, DATA_ZHA_GATEWAY, + DEFAULT_BAUDRATE, DEFAULT_RADIO_TYPE, DOMAIN, ENABLE_QUIRKS, RadioType) from .core.patches import apply_cluster_listener_patch +from .core.registries import establish_device_mappings REQUIREMENTS = [ 'bellows-homeassistant==0.7.2', @@ -143,9 +142,9 @@ async def async_setup_entry(hass, config_entry): async def async_zha_shutdown(event): """Handle shutdown tasks.""" + await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown() await hass.data[DATA_ZHA][ DATA_ZHA_GATEWAY].async_update_device_storage() - hass.data[DATA_ZHA][DATA_ZHA_RADIO].close() hass.bus.async_listen_once( ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown) @@ -154,6 +153,8 @@ async def async_zha_shutdown(event): async def async_unload_entry(hass, config_entry): """Unload ZHA config entry.""" + await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown() + api.async_unload_api(hass) dispatchers = hass.data[DATA_ZHA].get(DATA_ZHA_DISPATCHERS, []) @@ -170,11 +171,5 @@ async def async_unload_entry(hass, config_entry): for entity_id in entity_ids: await component.async_remove_entity(entity_id) - # clean up events - hass.data[DATA_ZHA][DATA_ZHA_CORE_EVENTS].clear() - - _LOGGER.debug("Closing zha radio") - hass.data[DATA_ZHA][DATA_ZHA_RADIO].close() - del hass.data[DATA_ZHA] return True diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index b7f418253d8d80..02f43a4bbf6def 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -17,7 +17,6 @@ DATA_ZHA = 'zha' DATA_ZHA_CONFIG = 'config' DATA_ZHA_BRIDGE_ID = 'zha_bridge_id' -DATA_ZHA_RADIO = 'zha_radio' DATA_ZHA_DISPATCHERS = 'zha_dispatchers' DATA_ZHA_CORE_COMPONENT = 'zha_core_component' DATA_ZHA_CORE_EVENTS = 'zha_core_events' diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 71e41c2509b504..83013b7bdf75fe 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -23,11 +23,11 @@ ADD_DEVICE_RELAY_LOGGERS, ATTR_MANUFACTURER, BELLOWS, CONF_BAUDRATE, CONF_DATABASE, CONF_RADIO_TYPE, CONF_USB_PATH, CONTROLLER, CURRENT, DATA_ZHA, DATA_ZHA_BRIDGE_ID, DATA_ZHA_CORE_COMPONENT, DATA_ZHA_GATEWAY, - DATA_ZHA_RADIO, DEBUG_LEVELS, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME, - DEVICE_FULL_INIT, DEVICE_INFO, DEVICE_JOINED, DEVICE_REMOVED, DOMAIN, IEEE, - LOG_ENTRY, LOG_OUTPUT, MODEL, NWK, ORIGINAL, RADIO, RADIO_DESCRIPTION, - RAW_INIT, SIGNAL_REMOVE, SIGNATURE, TYPE, ZHA, ZHA_GW_MSG, ZIGPY, - ZIGPY_DECONZ, ZIGPY_XBEE) + DEBUG_LEVELS, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME, DEVICE_FULL_INIT, + DEVICE_INFO, DEVICE_JOINED, DEVICE_REMOVED, DOMAIN, IEEE, LOG_ENTRY, + LOG_OUTPUT, MODEL, NWK, ORIGINAL, RADIO, RADIO_DESCRIPTION, RAW_INIT, + SIGNAL_REMOVE, SIGNATURE, TYPE, ZHA, ZHA_GW_MSG, ZIGPY, ZIGPY_DECONZ, + ZIGPY_XBEE) from .device import DeviceStatus, ZHADevice from .discovery import ( async_create_device_entity, async_dispatch_discovery_info, @@ -76,7 +76,6 @@ async def async_initialize(self, config_entry): radio = radio_details[RADIO] self.radio_description = RADIO_TYPES[radio_type][RADIO_DESCRIPTION] await radio.connect(usb_path, baudrate) - self._hass.data[DATA_ZHA][DATA_ZHA_RADIO] = radio if CONF_DATABASE in self._config: database = self._config[CONF_DATABASE] @@ -312,6 +311,11 @@ async def async_device_initialized(self, device, is_new_join): } ) + async def shutdown(self): + """Stop ZHA Controller Application.""" + _LOGGER.debug("Shutting down ZHA ControllerApplication") + await self.application_controller.shutdown() + @callback def async_capture_log_levels(): diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 695f2be5960ad1..ef7c2df6ce034d 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -139,7 +139,7 @@ async def check_zigpy_connection(usb_path, radio_type, database_path): await radio.connect(usb_path, DEFAULT_BAUDRATE) controller = ControllerApplication(radio, database_path) await asyncio.wait_for(controller.startup(auto_form=True), timeout=30) - radio.close() + await controller.shutdown() except Exception: # pylint: disable=broad-except return False return True From 048b100eea478665ded47a5f238fc68c96199436 Mon Sep 17 00:00:00 2001 From: Tobias Sauerwein Date: Wed, 3 Apr 2019 17:40:03 +0200 Subject: [PATCH 375/605] Clean up docstrings (#22679) * Clean up docstrings * Fix long lines * Fix more docstrings * Fix more docstrings * Fix more docstrings --- homeassistant/components/acer_projector/switch.py | 7 +------ homeassistant/components/actiontec/device_tracker.py | 7 +------ homeassistant/components/aftership/sensor.py | 7 +------ homeassistant/components/air_quality/__init__.py | 7 +------ homeassistant/components/airvisual/sensor.py | 7 +------ homeassistant/components/aladdin_connect/cover.py | 7 +------ .../components/alarm_control_panel/__init__.py | 7 +------ .../components/alarmdotcom/alarm_control_panel.py | 7 +------ homeassistant/components/alpha_vantage/sensor.py | 7 +------ homeassistant/components/amazon_polly/tts.py | 7 +------ homeassistant/components/androidtv/__init__.py | 7 +------ homeassistant/components/androidtv/media_player.py | 7 +------ homeassistant/components/anel_pwrctrl/switch.py | 7 +------ homeassistant/components/anthemav/media_player.py | 7 +------ homeassistant/components/apns/notify.py | 7 +------ homeassistant/components/aquostv/media_player.py | 7 +------ homeassistant/components/arest/binary_sensor.py | 7 +------ homeassistant/components/arest/sensor.py | 7 +------ homeassistant/components/arest/switch.py | 7 +------ homeassistant/components/aruba/device_tracker.py | 7 +------ homeassistant/components/arwn/sensor.py | 7 +------ homeassistant/components/asuswrt/device_tracker.py | 7 +------ homeassistant/components/asuswrt/sensor.py | 7 +------ homeassistant/components/aurora/binary_sensor.py | 7 +------ homeassistant/components/automatic/device_tracker.py | 7 +------ homeassistant/components/avion/light.py | 7 +------ homeassistant/components/awair/sensor.py | 7 +------ homeassistant/components/aws_lambda/notify.py | 7 +------ homeassistant/components/aws_sns/notify.py | 7 +------ homeassistant/components/aws_sqs/notify.py | 7 +------ homeassistant/components/baidu/tts.py | 8 +------- homeassistant/components/bayesian/binary_sensor.py | 7 +------ homeassistant/components/bbox/device_tracker.py | 7 +------ homeassistant/components/bbox/sensor.py | 7 +------ homeassistant/components/bh1750/sensor.py | 7 +------ homeassistant/components/binary_sensor/__init__.py | 7 +------ homeassistant/components/bitcoin/sensor.py | 7 +------ homeassistant/components/blackbird/media_player.py | 7 +------ homeassistant/components/blinksticklight/light.py | 7 +------ homeassistant/components/blinkt/light.py | 7 +------ homeassistant/components/blockchain/sensor.py | 7 +------ homeassistant/components/bluesound/media_player.py | 7 +------ .../components/bluetooth_le_tracker/device_tracker.py | 7 +------ .../components/bluetooth_tracker/device_tracker.py | 7 +------ homeassistant/components/bme280/sensor.py | 7 +------ homeassistant/components/bme680/sensor.py | 10 +--------- homeassistant/components/bom/sensor.py | 7 +------ homeassistant/components/braviatv/media_player.py | 7 +------ homeassistant/components/broadlink/sensor.py | 7 +------ homeassistant/components/broadlink/switch.py | 7 +------ homeassistant/components/brottsplatskartan/sensor.py | 7 +------ homeassistant/components/brunt/cover.py | 7 +------ .../components/bt_home_hub_5/device_tracker.py | 7 +------ homeassistant/components/bt_smarthub/device_tracker.py | 7 +------ homeassistant/components/buienradar/sensor.py | 7 +------ homeassistant/components/caldav/calendar.py | 7 +------ homeassistant/components/camera/__init__.py | 7 +------ homeassistant/components/canary/alarm_control_panel.py | 7 +------ homeassistant/components/canary/camera.py | 7 +------ homeassistant/components/canary/sensor.py | 7 +------ homeassistant/components/cert_expiry/sensor.py | 7 +------ homeassistant/components/channels/media_player.py | 7 +------ homeassistant/components/cisco_ios/device_tracker.py | 7 +------ homeassistant/components/ciscospark/notify.py | 7 +------ homeassistant/components/citybikes/sensor.py | 7 +------ homeassistant/components/clementine/media_player.py | 7 +------ homeassistant/components/clickatell/notify.py | 7 +------ homeassistant/components/clicksend/notify.py | 7 +------ homeassistant/components/clicksend_tts/notify.py | 9 +-------- homeassistant/components/climate/__init__.py | 7 +------ homeassistant/components/cmus/media_player.py | 7 +------ homeassistant/components/co2signal/sensor.py | 6 +----- homeassistant/components/coinbase/sensor.py | 7 +------ homeassistant/components/coinmarketcap/sensor.py | 7 +------ .../components/comed_hourly_pricing/sensor.py | 7 +------ homeassistant/components/command_line/binary_sensor.py | 7 +------ homeassistant/components/command_line/cover.py | 7 +------ homeassistant/components/command_line/notify.py | 7 +------ homeassistant/components/command_line/sensor.py | 7 +------ homeassistant/components/command_line/switch.py | 7 +------ .../components/concord232/alarm_control_panel.py | 7 +------ homeassistant/components/concord232/binary_sensor.py | 7 +------ homeassistant/components/coolmaster/climate.py | 7 +------ homeassistant/components/cover/__init__.py | 7 +------ .../components/cppm_tracker/device_tracker.py | 6 +----- homeassistant/components/cups/sensor.py | 7 +------ homeassistant/components/currencylayer/sensor.py | 7 +------ homeassistant/components/darksky/sensor.py | 7 +------ homeassistant/components/ddwrt/device_tracker.py | 7 +------ homeassistant/components/decora/light.py | 7 +------ homeassistant/components/decora_wifi/light.py | 10 +--------- homeassistant/components/deluge/sensor.py | 7 +------ homeassistant/components/deluge/switch.py | 7 +------ homeassistant/components/demo/air_quality.py | 7 +------ homeassistant/components/demo/alarm_control_panel.py | 7 +------ homeassistant/components/demo/binary_sensor.py | 7 +------ homeassistant/components/demo/calendar.py | 7 +------ homeassistant/components/demo/camera.py | 7 +------ homeassistant/components/demo/climate.py | 7 +------ homeassistant/components/demo/cover.py | 7 +------ homeassistant/components/demo/device_tracker.py | 7 +------ homeassistant/components/demo/fan.py | 7 +------ homeassistant/components/demo/image_processing.py | 7 +------ homeassistant/components/demo/light.py | 7 +------ homeassistant/components/demo/lock.py | 7 +------ homeassistant/components/demo/media_player.py | 7 +------ homeassistant/components/demo/notify.py | 7 +------ homeassistant/components/demo/remote.py | 7 +------ homeassistant/components/demo/sensor.py | 7 +------ homeassistant/components/demo/switch.py | 7 +------ homeassistant/components/demo/tts.py | 7 +------ homeassistant/components/demo/vacuum.py | 7 +------ homeassistant/components/denon/media_player.py | 7 +------ homeassistant/components/denonavr/media_player.py | 7 +------ homeassistant/components/device_tracker/__init__.py | 7 +------ homeassistant/components/dht/sensor.py | 7 +------ homeassistant/components/digitalloggers/switch.py | 7 +------ homeassistant/components/directv/media_player.py | 7 +------ homeassistant/components/discogs/sensor.py | 7 +------ homeassistant/components/discord/notify.py | 7 +------ .../components/dlib_face_detect/image_processing.py | 7 +------ .../components/dlib_face_identify/image_processing.py | 7 +------ homeassistant/components/dlink/switch.py | 7 +------ homeassistant/components/dlna_dmr/media_player.py | 7 +------ homeassistant/components/dnsip/sensor.py | 7 +------ homeassistant/components/dsmr/sensor.py | 7 +------ homeassistant/components/dte_energy_bridge/sensor.py | 7 +------ homeassistant/components/duke_energy/sensor.py | 7 +------ homeassistant/components/dunehd/media_player.py | 7 +------ homeassistant/components/dyson/climate.py | 7 +------ homeassistant/components/dyson/sensor.py | 7 +------ homeassistant/components/dyson/vacuum.py | 7 +------ homeassistant/components/edimax/switch.py | 7 +------ .../components/ee_brightbox/device_tracker.py | 7 +------ homeassistant/components/efergy/sensor.py | 7 +------ homeassistant/components/eliqonline/sensor.py | 7 +------ homeassistant/components/emby/media_player.py | 7 +------ homeassistant/components/emoncms/sensor.py | 7 +------ homeassistant/components/enphase_envoy/sensor.py | 7 +------ homeassistant/components/envirophat/sensor.py | 7 +------ homeassistant/components/ephember/climate.py | 7 +------ homeassistant/components/epson/media_player.py | 7 +------ homeassistant/components/eq3btsmart/climate.py | 7 +------ homeassistant/components/everlights/light.py | 7 +------ homeassistant/components/facebook/notify.py | 7 +------ homeassistant/components/facebox/image_processing.py | 7 +------ homeassistant/components/familyhub/camera.py | 7 +------ homeassistant/components/fan/__init__.py | 7 +------ homeassistant/components/fedex/sensor.py | 7 +------ homeassistant/components/ffmpeg/camera.py | 7 +------ .../components/ffmpeg_motion/binary_sensor.py | 7 +------ homeassistant/components/ffmpeg_noise/binary_sensor.py | 7 +------ homeassistant/components/file/notify.py | 7 +------ homeassistant/components/file/sensor.py | 7 +------ homeassistant/components/filesize/sensor.py | 7 +------ homeassistant/components/filter/sensor.py | 7 +------ homeassistant/components/fints/sensor.py | 7 +------ homeassistant/components/fitbit/sensor.py | 7 +------ homeassistant/components/fixer/sensor.py | 7 +------ homeassistant/components/flic/binary_sensor.py | 7 +------ homeassistant/components/flock/notify.py | 7 +------ homeassistant/components/flunearyou/sensor.py | 7 +------ homeassistant/components/flux_led/light.py | 7 +------ homeassistant/components/folder/sensor.py | 7 +------ homeassistant/components/foobot/sensor.py | 7 +------ homeassistant/components/foscam/camera.py | 7 +------ homeassistant/components/free_mobile/notify.py | 7 +------ homeassistant/components/freedns/__init__.py | 7 +------ homeassistant/components/fritz/device_tracker.py | 7 +------ .../components/fritzbox_callmonitor/sensor.py | 7 +------ homeassistant/components/fritzbox_netmonitor/sensor.py | 7 +------ homeassistant/components/fritzdect/switch.py | 7 +------ .../components/frontier_silicon/media_player.py | 7 +------ homeassistant/components/futurenow/light.py | 7 +------ homeassistant/components/garadget/cover.py | 7 +------ homeassistant/components/gc100/binary_sensor.py | 7 +------ homeassistant/components/gc100/switch.py | 7 +------ homeassistant/components/gearbest/sensor.py | 7 +------ homeassistant/components/geizhals/sensor.py | 7 +------ homeassistant/components/generic/camera.py | 7 +------ homeassistant/components/generic_thermostat/climate.py | 7 +------ homeassistant/components/geofency/device_tracker.py | 7 +------ homeassistant/components/github/sensor.py | 7 +------ homeassistant/components/gitlab_ci/sensor.py | 7 +------ homeassistant/components/gitter/sensor.py | 7 +------ homeassistant/components/glances/sensor.py | 7 +------ homeassistant/components/gntp/notify.py | 7 +------ homeassistant/components/gogogate2/cover.py | 7 +------ homeassistant/components/google_assistant/http.py | 7 +------ homeassistant/components/google_maps/device_tracker.py | 7 +------ homeassistant/components/google_travel_time/sensor.py | 7 +------ homeassistant/components/google_wifi/sensor.py | 7 +------ homeassistant/components/gpmdp/media_player.py | 7 +------ homeassistant/components/gpsd/sensor.py | 7 +------ homeassistant/components/greeneye_monitor/sensor.py | 7 +------ homeassistant/components/greenwave/light.py | 7 +------ homeassistant/components/group/cover.py | 7 +------ homeassistant/components/group/light.py | 7 +------ homeassistant/components/group/notify.py | 7 +------ homeassistant/components/gstreamer/media_player.py | 7 +------ homeassistant/components/gtfs/sensor.py | 7 +------ homeassistant/components/gtt/sensor.py | 7 +------ .../components/harman_kardon_avr/media_player.py | 7 +------ homeassistant/components/haveibeenpwned/sensor.py | 7 +------ homeassistant/components/hddtemp/sensor.py | 7 +------ homeassistant/components/heatmiser/climate.py | 7 +------ homeassistant/components/hikvision/binary_sensor.py | 7 +------ homeassistant/components/hikvisioncam/switch.py | 7 +------ homeassistant/components/hipchat/notify.py | 7 +------ homeassistant/components/history_stats/sensor.py | 7 +------ homeassistant/components/hitron_coda/device_tracker.py | 7 +------ homeassistant/components/homematic/notify.py | 7 +------ homeassistant/components/honeywell/climate.py | 7 +------ homeassistant/components/hook/switch.py | 7 +------ homeassistant/components/horizon/media_player.py | 7 +------ homeassistant/components/html5/notify.py | 7 +------ homeassistant/components/htu21d/sensor.py | 7 +------ .../components/huawei_router/device_tracker.py | 7 +------ homeassistant/components/ialarm/alarm_control_panel.py | 7 +------ homeassistant/components/icloud/device_tracker.py | 7 +------ homeassistant/components/iglo/light.py | 7 +------ homeassistant/components/image_processing/__init__.py | 7 +------ homeassistant/components/imap/sensor.py | 7 +------ homeassistant/components/imap_email_content/sensor.py | 7 +------ homeassistant/components/influxdb/sensor.py | 7 +------ homeassistant/components/integration/sensor.py | 7 +------ .../components/islamic_prayer_times/sensor.py | 7 +------ homeassistant/components/iss/binary_sensor.py | 7 +------ homeassistant/components/itunes/media_player.py | 7 +------ homeassistant/components/jewish_calendar/sensor.py | 7 +------ homeassistant/components/kankun/switch.py | 7 +------ .../components/keenetic_ndms2/device_tracker.py | 7 +------ homeassistant/components/kiwi/lock.py | 7 +------ homeassistant/components/kodi/media_player.py | 7 +------ homeassistant/components/kodi/notify.py | 7 +------ homeassistant/components/kwb/sensor.py | 7 +------ homeassistant/components/lacrosse/sensor.py | 7 +------ homeassistant/components/lannouncer/notify.py | 7 +------ homeassistant/components/launch_library/sensor.py | 7 +------ homeassistant/components/lg_netcast/media_player.py | 7 +------ homeassistant/components/lg_soundbar/media_player.py | 7 +------ homeassistant/components/light/__init__.py | 7 +------ homeassistant/components/limitlessled/light.py | 7 +------ homeassistant/components/linksys_ap/device_tracker.py | 7 +------ homeassistant/components/linky/sensor.py | 7 +------ homeassistant/components/linux_battery/sensor.py | 7 +------ homeassistant/components/litejet/light.py | 7 +------ homeassistant/components/litejet/switch.py | 7 +------ homeassistant/components/liveboxplaytv/media_player.py | 7 +------ homeassistant/components/llamalab_automate/notify.py | 7 +------ homeassistant/components/local_file/camera.py | 7 +------ homeassistant/components/locative/device_tracker.py | 7 +------ homeassistant/components/lock/__init__.py | 7 +------ homeassistant/components/lockitron/lock.py | 7 +------ homeassistant/components/london_air/sensor.py | 7 +------ homeassistant/components/london_underground/sensor.py | 7 +------ homeassistant/components/loopenergy/sensor.py | 7 +------ homeassistant/components/luci/device_tracker.py | 7 +------ homeassistant/components/lw12wifi/light.py | 7 +------ homeassistant/components/lyft/sensor.py | 7 +------ homeassistant/components/magicseaweed/sensor.py | 7 +------ homeassistant/components/manual/alarm_control_panel.py | 7 +------ .../components/manual_mqtt/alarm_control_panel.py | 7 +------ homeassistant/components/marytts/tts.py | 7 +------ homeassistant/components/mastodon/notify.py | 7 +------ homeassistant/components/media_player/__init__.py | 7 +------ homeassistant/components/mediaroom/media_player.py | 7 +------ homeassistant/components/melissa/climate.py | 7 +------ homeassistant/components/message_bird/notify.py | 7 +------ homeassistant/components/metoffice/sensor.py | 7 +------ homeassistant/components/mfi/sensor.py | 7 +------ homeassistant/components/mfi/switch.py | 7 +------ homeassistant/components/mhz19/sensor.py | 7 +------ homeassistant/components/microsoft/tts.py | 7 +------ .../microsoft_face_detect/image_processing.py | 7 +------ .../microsoft_face_identify/image_processing.py | 7 +------ homeassistant/components/miflora/sensor.py | 7 +------ homeassistant/components/mikrotik/device_tracker.py | 7 +------ homeassistant/components/mill/climate.py | 7 +------ homeassistant/components/min_max/sensor.py | 7 +------ homeassistant/components/mitemp_bt/sensor.py | 7 +------ homeassistant/components/mjpeg/camera.py | 7 +------ homeassistant/components/modem_callerid/sensor.py | 7 +------ homeassistant/components/mold_indicator/sensor.py | 7 +------ homeassistant/components/monoprice/media_player.py | 7 +------ homeassistant/components/moon/sensor.py | 7 +------ homeassistant/components/mpchc/media_player.py | 7 +------ homeassistant/components/mpd/media_player.py | 7 +------ homeassistant/components/mqtt/__init__.py | 7 +------ homeassistant/components/mqtt/alarm_control_panel.py | 7 +------ homeassistant/components/mqtt/binary_sensor.py | 7 +------ homeassistant/components/mqtt/camera.py | 7 +------ homeassistant/components/mqtt/climate.py | 7 +------ homeassistant/components/mqtt/cover.py | 7 +------ homeassistant/components/mqtt/device_tracker.py | 7 +------ homeassistant/components/mqtt/discovery.py | 7 +------ homeassistant/components/mqtt/fan.py | 7 +------ homeassistant/components/mqtt/lock.py | 7 +------ homeassistant/components/mqtt/sensor.py | 7 +------ homeassistant/components/mqtt/server.py | 7 +------ homeassistant/components/mqtt/subscription.py | 7 +------ homeassistant/components/mqtt/switch.py | 7 +------ homeassistant/components/mqtt/vacuum.py | 7 +------ homeassistant/components/mqtt_json/device_tracker.py | 7 +------ homeassistant/components/mqtt_room/sensor.py | 7 +------ homeassistant/components/mvglive/sensor.py | 7 +------ homeassistant/components/mycroft/notify.py | 7 +------ homeassistant/components/myq/cover.py | 7 +------ homeassistant/components/mystrom/binary_sensor.py | 7 +------ homeassistant/components/nad/media_player.py | 7 +------ homeassistant/components/nanoleaf/light.py | 7 +------ .../components/nederlandse_spoorwegen/sensor.py | 7 +------ homeassistant/components/nello/lock.py | 7 +------ .../components/ness_alarm/alarm_control_panel.py | 7 +------ homeassistant/components/ness_alarm/binary_sensor.py | 7 +------ homeassistant/components/netatmo_public/sensor.py | 7 +------ homeassistant/components/netdata/sensor.py | 7 +------ homeassistant/components/netgear/device_tracker.py | 7 +------ homeassistant/components/netio/switch.py | 7 +------ homeassistant/components/neurio_energy/sensor.py | 7 +------ homeassistant/components/nfandroidtv/notify.py | 7 +------ homeassistant/components/niko_home_control/light.py | 7 +------ homeassistant/components/nilu/air_quality.py | 7 +------ homeassistant/components/nmbs/sensor.py | 7 +------ homeassistant/components/noaa_tides/sensor.py | 7 +------ homeassistant/components/norway_air/air_quality.py | 7 +------ homeassistant/components/notify/__init__.py | 7 +------ homeassistant/components/nsw_fuel_station/sensor.py | 7 +------ homeassistant/components/nuheat/climate.py | 7 +------ homeassistant/components/nuki/lock.py | 7 +------ homeassistant/components/nut/sensor.py | 7 +------ homeassistant/components/nx584/alarm_control_panel.py | 7 +------ homeassistant/components/nx584/binary_sensor.py | 7 +------ homeassistant/components/nzbget/sensor.py | 7 +------ homeassistant/components/ohmconnect/sensor.py | 7 +------ homeassistant/components/onewire/sensor.py | 7 +------ homeassistant/components/onkyo/media_player.py | 7 +------ homeassistant/components/onvif/camera.py | 7 +------ .../components/openalpr_cloud/image_processing.py | 7 +------ .../components/openalpr_local/image_processing.py | 7 +------ homeassistant/components/openevse/sensor.py | 7 +------ homeassistant/components/openexchangerates/sensor.py | 7 +------ homeassistant/components/opengarage/cover.py | 7 +------ homeassistant/components/openhardwaremonitor/sensor.py | 7 +------ homeassistant/components/openhome/media_player.py | 7 +------ homeassistant/components/opensky/sensor.py | 7 +------ homeassistant/components/openweathermap/sensor.py | 7 +------ homeassistant/components/opple/light.py | 7 +------ homeassistant/components/orvibo/switch.py | 7 +------ homeassistant/components/osramlightify/light.py | 7 +------ homeassistant/components/otp/sensor.py | 7 +------ homeassistant/components/owntracks/device_tracker.py | 7 +------ .../components/panasonic_bluray/media_player.py | 7 +------ .../components/panasonic_viera/media_player.py | 7 +------ homeassistant/components/pandora/media_player.py | 7 +------ homeassistant/components/philips_js/media_player.py | 7 +------ homeassistant/components/pi_hole/sensor.py | 7 +------ homeassistant/components/picotts/tts.py | 7 +------ homeassistant/components/piglow/light.py | 7 +------ homeassistant/components/pilight/__init__.py | 7 +------ homeassistant/components/pilight/binary_sensor.py | 7 +------ homeassistant/components/pilight/sensor.py | 7 +------ homeassistant/components/pilight/switch.py | 7 +------ homeassistant/components/ping/binary_sensor.py | 7 +------ homeassistant/components/ping/device_tracker.py | 7 +------ homeassistant/components/pioneer/media_player.py | 7 +------ homeassistant/components/pjlink/media_player.py | 7 +------ homeassistant/components/plex/media_player.py | 7 +------ homeassistant/components/plex/sensor.py | 7 +------ homeassistant/components/pocketcasts/sensor.py | 7 +------ homeassistant/components/postnl/sensor.py | 7 +------ homeassistant/components/prezzibenzina/sensor.py | 7 +------ homeassistant/components/proliphix/climate.py | 7 +------ homeassistant/components/prowl/notify.py | 7 +------ homeassistant/components/proxy/camera.py | 7 +------ homeassistant/components/ps4/__init__.py | 7 +------ homeassistant/components/ps4/media_player.py | 7 +------ homeassistant/components/pulseaudio_loopback/switch.py | 7 +------ homeassistant/components/push/camera.py | 7 +------ homeassistant/components/pushbullet/notify.py | 7 +------ homeassistant/components/pushbullet/sensor.py | 7 +------ homeassistant/components/pushetta/notify.py | 7 +------ homeassistant/components/pushover/notify.py | 7 +------ homeassistant/components/pushsafer/notify.py | 7 +------ homeassistant/components/pvoutput/sensor.py | 7 +------ homeassistant/components/pyload/sensor.py | 7 +------ homeassistant/components/python_script/__init__.py | 7 +------ homeassistant/components/qbittorrent/sensor.py | 7 +------ homeassistant/components/qnap/sensor.py | 7 +------ homeassistant/components/qrcode/image_processing.py | 7 +------ .../components/quantum_gateway/device_tracker.py | 7 +------ homeassistant/components/qwikswitch/__init__.py | 7 +------ homeassistant/components/qwikswitch/binary_sensor.py | 7 +------ homeassistant/components/qwikswitch/light.py | 7 +------ homeassistant/components/qwikswitch/sensor.py | 7 +------ homeassistant/components/qwikswitch/switch.py | 7 +------ homeassistant/components/rachio/__init__.py | 7 +------ homeassistant/components/rachio/binary_sensor.py | 7 +------ homeassistant/components/rachio/switch.py | 7 +------ homeassistant/components/radarr/sensor.py | 7 +------ homeassistant/components/radiotherm/climate.py | 7 +------ homeassistant/components/rainbird/__init__.py | 7 +------ homeassistant/components/rainbird/sensor.py | 7 +------ homeassistant/components/rainbird/switch.py | 7 +------ homeassistant/components/raincloud/__init__.py | 7 +------ homeassistant/components/raincloud/binary_sensor.py | 7 +------ homeassistant/components/raincloud/sensor.py | 7 +------ homeassistant/components/rainmachine/binary_sensor.py | 7 +------ homeassistant/components/rainmachine/sensor.py | 7 +------ homeassistant/components/rainmachine/switch.py | 7 +------ homeassistant/components/random/binary_sensor.py | 7 +------ homeassistant/components/random/sensor.py | 7 +------ homeassistant/components/raspyrfm/switch.py | 7 +------ homeassistant/components/recollect_waste/sensor.py | 7 +------ homeassistant/components/recswitch/switch.py | 7 +------ homeassistant/components/rest/binary_sensor.py | 7 +------ homeassistant/components/rest/notify.py | 7 +------ homeassistant/components/rest/sensor.py | 7 +------ homeassistant/components/rest/switch.py | 7 +------ homeassistant/components/rflink/binary_sensor.py | 7 +------ homeassistant/components/rflink/cover.py | 7 +------ homeassistant/components/rflink/light.py | 7 +------ homeassistant/components/rflink/sensor.py | 7 +------ homeassistant/components/rflink/switch.py | 7 +------ homeassistant/components/ring/binary_sensor.py | 7 +------ homeassistant/components/ring/camera.py | 7 +------ homeassistant/components/ring/sensor.py | 7 +------ homeassistant/components/ritassist/device_tracker.py | 7 +------ homeassistant/components/rmvtransport/sensor.py | 7 +------ homeassistant/components/rocketchat/notify.py | 7 +------ homeassistant/components/roomba/vacuum.py | 7 +------ homeassistant/components/rova/sensor.py | 7 +------ homeassistant/components/rpi_camera/camera.py | 7 +------ homeassistant/components/rpi_rf/switch.py | 7 +------ homeassistant/components/russound_rio/media_player.py | 7 +------ homeassistant/components/russound_rnet/media_player.py | 7 +------ homeassistant/components/ruter/sensor.py | 7 +------ homeassistant/components/samsungtv/media_player.py | 7 +------ homeassistant/components/scrape/sensor.py | 7 +------ homeassistant/components/scsgate/__init__.py | 7 +------ homeassistant/components/season/sensor.py | 7 +------ homeassistant/components/sendgrid/notify.py | 7 +------ homeassistant/components/sensehat/light.py | 7 +------ homeassistant/components/sensehat/sensor.py | 7 +------ homeassistant/components/sensibo/climate.py | 7 +------ homeassistant/components/sensor/__init__.py | 7 +------ homeassistant/components/serial/sensor.py | 7 +------ homeassistant/components/serial_pm/sensor.py | 7 +------ homeassistant/components/sesame/lock.py | 7 +------ .../components/seven_segments/image_processing.py | 7 +------ homeassistant/components/seventeentrack/sensor.py | 7 +------ homeassistant/components/sht31/sensor.py | 7 +------ homeassistant/components/sigfox/sensor.py | 7 +------ homeassistant/components/simplepush/notify.py | 7 +------ homeassistant/components/simulated/sensor.py | 7 +------ homeassistant/components/sky_hub/device_tracker.py | 7 +------ homeassistant/components/skybeacon/sensor.py | 7 +------ homeassistant/components/slack/notify.py | 7 +------ homeassistant/components/sleepiq/binary_sensor.py | 7 +------ homeassistant/components/sleepiq/sensor.py | 7 +------ homeassistant/components/sma/sensor.py | 7 +------ homeassistant/components/smartthings/smartapp.py | 8 +------- homeassistant/components/smtp/notify.py | 7 +------ homeassistant/components/snapcast/media_player.py | 7 +------ homeassistant/components/snmp/device_tracker.py | 7 +------ homeassistant/components/snmp/sensor.py | 7 +------ homeassistant/components/snmp/switch.py | 7 +------ homeassistant/components/socialblade/sensor.py | 7 +------ homeassistant/components/solaredge/sensor.py | 7 +------ homeassistant/components/sonarr/sensor.py | 7 +------ homeassistant/components/songpal/media_player.py | 7 +------ homeassistant/components/soundtouch/media_player.py | 7 +------ homeassistant/components/spc/alarm_control_panel.py | 7 +------ homeassistant/components/spc/binary_sensor.py | 7 +------ homeassistant/components/spotcrime/sensor.py | 7 +------ homeassistant/components/spotify/media_player.py | 7 +------ homeassistant/components/squeezebox/media_player.py | 7 +------ homeassistant/components/srp_energy/sensor.py | 7 +------ homeassistant/components/starlingbank/sensor.py | 7 +------ homeassistant/components/startca/sensor.py | 7 +------ homeassistant/components/statistics/sensor.py | 7 +------ homeassistant/components/steam_online/sensor.py | 7 +------ homeassistant/components/stream/__init__.py | 7 +------ homeassistant/components/stream/hls.py | 7 +------ homeassistant/components/stride/notify.py | 7 +------ homeassistant/components/supervisord/sensor.py | 7 +------ .../components/swiss_hydrological_data/sensor.py | 7 +------ .../components/swiss_public_transport/sensor.py | 7 +------ homeassistant/components/swisscom/device_tracker.py | 7 +------ homeassistant/components/switch/__init__.py | 7 +------ homeassistant/components/switch/light.py | 7 +------ homeassistant/components/switchmate/switch.py | 7 +------ homeassistant/components/syncthru/sensor.py | 7 +------ homeassistant/components/synology/camera.py | 7 +------ homeassistant/components/synology_chat/notify.py | 7 +------ homeassistant/components/syslog/notify.py | 7 +------ homeassistant/components/sytadin/sensor.py | 7 +------ homeassistant/components/tank_utility/sensor.py | 7 +------ homeassistant/components/tapsaff/binary_sensor.py | 7 +------ homeassistant/components/tautulli/sensor.py | 7 +------ homeassistant/components/tcp/binary_sensor.py | 7 +------ homeassistant/components/tcp/sensor.py | 7 +------ homeassistant/components/ted5000/sensor.py | 7 +------ homeassistant/components/teksavvy/sensor.py | 7 +------ homeassistant/components/telegram/notify.py | 7 +------ homeassistant/components/telnet/switch.py | 7 +------ homeassistant/components/temper/sensor.py | 7 +------ homeassistant/components/template/binary_sensor.py | 7 +------ homeassistant/components/template/cover.py | 7 +------ homeassistant/components/template/fan.py | 7 +------ homeassistant/components/template/light.py | 7 +------ homeassistant/components/template/lock.py | 7 +------ homeassistant/components/template/sensor.py | 7 +------ homeassistant/components/template/switch.py | 7 +------ homeassistant/components/thomson/device_tracker.py | 7 +------ homeassistant/components/threshold/binary_sensor.py | 7 +------ homeassistant/components/tikteck/light.py | 7 +------ homeassistant/components/tile/device_tracker.py | 7 +------ homeassistant/components/time_date/sensor.py | 7 +------ homeassistant/components/todoist/calendar.py | 7 +------ homeassistant/components/tomato/device_tracker.py | 7 +------ homeassistant/components/torque/sensor.py | 7 +------ .../components/totalconnect/alarm_control_panel.py | 7 +------ homeassistant/components/touchline/climate.py | 7 +------ homeassistant/components/tplink/device_tracker.py | 7 +------ homeassistant/components/tplink/light.py | 7 +------ homeassistant/components/tplink/switch.py | 7 +------ homeassistant/components/traccar/device_tracker.py | 7 +------ homeassistant/components/trackr/device_tracker.py | 7 +------ .../components/trafikverket_weatherstation/sensor.py | 7 +------ homeassistant/components/travisci/sensor.py | 7 +------ homeassistant/components/tts/__init__.py | 7 +------ homeassistant/components/twilio_call/notify.py | 7 +------ homeassistant/components/twilio_sms/notify.py | 7 +------ homeassistant/components/twitch/sensor.py | 7 +------ homeassistant/components/twitter/notify.py | 7 +------ homeassistant/components/ubee/device_tracker.py | 7 +------ homeassistant/components/uber/sensor.py | 7 +------ homeassistant/components/ubus/device_tracker.py | 7 +------ .../components/ue_smart_radio/media_player.py | 7 +------ homeassistant/components/unifi/device_tracker.py | 7 +------ .../components/unifi_direct/device_tracker.py | 7 +------ homeassistant/components/universal/media_player.py | 7 +------ homeassistant/components/upc_connect/device_tracker.py | 7 +------ homeassistant/components/upnp/sensor.py | 7 +------ homeassistant/components/ups/sensor.py | 7 +------ homeassistant/components/uptime/sensor.py | 7 +------ homeassistant/components/uptimerobot/binary_sensor.py | 7 +------ homeassistant/components/uscis/sensor.py | 7 +------ homeassistant/components/uvc/camera.py | 7 +------ homeassistant/components/vacuum/__init__.py | 7 +------ homeassistant/components/vasttrafik/sensor.py | 7 +------ homeassistant/components/venstar/climate.py | 7 +------ homeassistant/components/version/sensor.py | 7 +------ homeassistant/components/vesync/switch.py | 7 +------ homeassistant/components/viaggiatreno/sensor.py | 7 +------ homeassistant/components/vizio/media_player.py | 7 +------ homeassistant/components/vlc/media_player.py | 7 +------ homeassistant/components/voicerss/tts.py | 7 +------ homeassistant/components/volkszaehler/sensor.py | 7 +------ homeassistant/components/vultr/binary_sensor.py | 7 +------ homeassistant/components/vultr/sensor.py | 7 +------ homeassistant/components/vultr/switch.py | 7 +------ homeassistant/components/wake_on_lan/switch.py | 7 +------ homeassistant/components/waqi/sensor.py | 7 +------ homeassistant/components/waterfurnace/sensor.py | 7 +------ homeassistant/components/waze_travel_time/sensor.py | 7 +------ homeassistant/components/whois/sensor.py | 7 +------ homeassistant/components/worldclock/sensor.py | 7 +------ homeassistant/components/worldtidesinfo/sensor.py | 7 +------ homeassistant/components/worxlandroid/sensor.py | 7 +------ homeassistant/components/wsdot/sensor.py | 7 +------ homeassistant/components/wunderground/sensor.py | 7 +------ homeassistant/components/x10/light.py | 7 +------ homeassistant/components/xbox_live/sensor.py | 7 +------ homeassistant/components/xeoma/camera.py | 7 +------ homeassistant/components/xiaomi/camera.py | 7 +------ homeassistant/components/xiaomi/device_tracker.py | 7 +------ homeassistant/components/xiaomi_tv/media_player.py | 7 +------ homeassistant/components/xmpp/notify.py | 7 +------ .../components/yale_smart_alarm/alarm_control_panel.py | 7 +------ homeassistant/components/yamaha/media_player.py | 7 +------ .../components/yamaha_musiccast/media_player.py | 7 +------ homeassistant/components/yandextts/tts.py | 7 +------ homeassistant/components/yeelightsunflower/light.py | 7 +------ homeassistant/components/yessssms/notify.py | 7 +------ homeassistant/components/yi/camera.py | 7 +------ homeassistant/components/yr/sensor.py | 7 +------ homeassistant/components/yweather/sensor.py | 7 +------ homeassistant/components/zamg/sensor.py | 7 +------ homeassistant/components/zengge/light.py | 7 +------ homeassistant/components/zestimate/sensor.py | 7 +------ homeassistant/components/zha/__init__.py | 7 +------ homeassistant/components/zha/api.py | 7 +------ homeassistant/components/zha/binary_sensor.py | 7 +------ homeassistant/components/zha/const.py | 7 +------ homeassistant/components/zha/device_entity.py | 7 +------ homeassistant/components/zha/entity.py | 7 +------ homeassistant/components/zha/fan.py | 7 +------ homeassistant/components/zha/light.py | 7 +------ homeassistant/components/zha/sensor.py | 7 +------ homeassistant/components/zha/switch.py | 7 +------ homeassistant/components/zhong_hong/climate.py | 7 +------ .../components/ziggo_mediabox_xl/media_player.py | 7 +------ 604 files changed, 604 insertions(+), 3632 deletions(-) diff --git a/homeassistant/components/acer_projector/switch.py b/homeassistant/components/acer_projector/switch.py index 7abb3d1edbc846..df6fb8816aae47 100644 --- a/homeassistant/components/acer_projector/switch.py +++ b/homeassistant/components/acer_projector/switch.py @@ -1,9 +1,4 @@ -""" -Use serial protocol of Acer projector to obtain state of the projector. - -For more details about this component, please refer to the documentation -at https://home-assistant.io/components/switch.acer_projector/ -""" +"""Use serial protocol of Acer projector to obtain state of the projector.""" import logging import re diff --git a/homeassistant/components/actiontec/device_tracker.py b/homeassistant/components/actiontec/device_tracker.py index 72d9992c60f3e2..3f0c87867943a8 100644 --- a/homeassistant/components/actiontec/device_tracker.py +++ b/homeassistant/components/actiontec/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Actiontec MI424WR (Verizon FIOS) routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.actiontec/ -""" +"""Support for Actiontec MI424WR (Verizon FIOS) routers.""" import logging import re import telnetlib diff --git a/homeassistant/components/aftership/sensor.py b/homeassistant/components/aftership/sensor.py index eb5188a95cb869..18bc3cb34304bc 100644 --- a/homeassistant/components/aftership/sensor.py +++ b/homeassistant/components/aftership/sensor.py @@ -1,9 +1,4 @@ -""" -Support for non-delivered packages recorded in AfterShip. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.aftership/ -""" +"""Support for non-delivered packages recorded in AfterShip.""" from datetime import timedelta import logging diff --git a/homeassistant/components/air_quality/__init__.py b/homeassistant/components/air_quality/__init__.py index 66af51efcb1830..87d5c3b6bd41b2 100644 --- a/homeassistant/components/air_quality/__init__.py +++ b/homeassistant/components/air_quality/__init__.py @@ -1,9 +1,4 @@ -""" -Component for handling Air Quality data for your location. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/air_quality/ -""" +"""Component for handling Air Quality data for your location.""" from datetime import timedelta import logging diff --git a/homeassistant/components/airvisual/sensor.py b/homeassistant/components/airvisual/sensor.py index b9e7a3315e3e99..7fad7bb35be0ec 100644 --- a/homeassistant/components/airvisual/sensor.py +++ b/homeassistant/components/airvisual/sensor.py @@ -1,9 +1,4 @@ -""" -Support for AirVisual air quality sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.airvisual/ -""" +"""Support for AirVisual air quality sensors.""" from logging import getLogger from datetime import timedelta diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 4627ba7778148e..01146fecbb667c 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -1,9 +1,4 @@ -""" -Platform for the Aladdin Connect cover component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/cover.aladdin_connect/ -""" +"""Platform for the Aladdin Connect cover component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 86bb3e73bdab99..36a68eda174b32 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with an alarm control panel. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel/ -""" +"""Component to interface with an alarm control panel.""" from datetime import timedelta import logging diff --git a/homeassistant/components/alarmdotcom/alarm_control_panel.py b/homeassistant/components/alarmdotcom/alarm_control_panel.py index 4f2913771b1ee0..ea581aca747fc8 100644 --- a/homeassistant/components/alarmdotcom/alarm_control_panel.py +++ b/homeassistant/components/alarmdotcom/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Interfaces with Alarm.com alarm control panels. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.alarmdotcom/ -""" +"""Interfaces with Alarm.com alarm control panels.""" import logging import re diff --git a/homeassistant/components/alpha_vantage/sensor.py b/homeassistant/components/alpha_vantage/sensor.py index 774a3fe95f6b9d..0eb57e5b27aeee 100644 --- a/homeassistant/components/alpha_vantage/sensor.py +++ b/homeassistant/components/alpha_vantage/sensor.py @@ -1,9 +1,4 @@ -""" -Stock market information from Alpha Vantage. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.alpha_vantage/ -""" +"""Stock market information from Alpha Vantage.""" from datetime import timedelta import logging diff --git a/homeassistant/components/amazon_polly/tts.py b/homeassistant/components/amazon_polly/tts.py index 167cd9cfc784b0..d29ae32fb5723f 100644 --- a/homeassistant/components/amazon_polly/tts.py +++ b/homeassistant/components/amazon_polly/tts.py @@ -1,9 +1,4 @@ -""" -Support for the Amazon Polly text to speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.amazon_polly/ -""" +"""Support for the Amazon Polly text to speech service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/androidtv/__init__.py b/homeassistant/components/androidtv/__init__.py index fd108e05973a1c..14832aef315870 100644 --- a/homeassistant/components/androidtv/__init__.py +++ b/homeassistant/components/androidtv/__init__.py @@ -1,6 +1 @@ -""" -Support for functionality to interact with Android TV and Fire TV devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.androidtv/ -""" +"""Support for functionality to interact with Android TV/Fire TV devices.""" diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 0129b547acf505..a62a7f2a6d978f 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for functionality to interact with Android TV and Fire TV devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.androidtv/ -""" +"""Support for functionality to interact with Android TV / Fire TV devices.""" import functools import logging import voluptuous as vol diff --git a/homeassistant/components/anel_pwrctrl/switch.py b/homeassistant/components/anel_pwrctrl/switch.py index fadb3cd96ff909..b9b3070b97e737 100644 --- a/homeassistant/components/anel_pwrctrl/switch.py +++ b/homeassistant/components/anel_pwrctrl/switch.py @@ -1,9 +1,4 @@ -""" -Support for ANEL PwrCtrl switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.pwrctrl/ -""" +"""Support for ANEL PwrCtrl switches.""" import logging import socket from datetime import timedelta diff --git a/homeassistant/components/anthemav/media_player.py b/homeassistant/components/anthemav/media_player.py index 36bc5ae10e1dcd..c7ee579bc17cb0 100644 --- a/homeassistant/components/anthemav/media_player.py +++ b/homeassistant/components/anthemav/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Anthem Network Receivers and Processors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.anthemav/ -""" +"""Support for Anthem Network Receivers and Processors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/apns/notify.py b/homeassistant/components/apns/notify.py index b2c6b63864f273..d7f6559fe7eaad 100644 --- a/homeassistant/components/apns/notify.py +++ b/homeassistant/components/apns/notify.py @@ -1,9 +1,4 @@ -""" -APNS Notification platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.apns/ -""" +"""APNS Notification platform.""" import logging import os diff --git a/homeassistant/components/aquostv/media_player.py b/homeassistant/components/aquostv/media_player.py index 59723b47522601..0ffe48d21ec61f 100644 --- a/homeassistant/components/aquostv/media_player.py +++ b/homeassistant/components/aquostv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with an Aquos TV. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.aquostv/ -""" +"""Support for interface with an Aquos TV.""" import logging import voluptuous as vol diff --git a/homeassistant/components/arest/binary_sensor.py b/homeassistant/components/arest/binary_sensor.py index b70620df3e21b2..3fd669a2bba3ad 100644 --- a/homeassistant/components/arest/binary_sensor.py +++ b/homeassistant/components/arest/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for an exposed aREST RESTful API of a device. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.arest/ -""" +"""Support for an exposed aREST RESTful API of a device.""" import logging from datetime import timedelta diff --git a/homeassistant/components/arest/sensor.py b/homeassistant/components/arest/sensor.py index e0c5ef129cee36..fc443cd60b652b 100644 --- a/homeassistant/components/arest/sensor.py +++ b/homeassistant/components/arest/sensor.py @@ -1,9 +1,4 @@ -""" -Support for an exposed aREST RESTful API of a device. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.arest/ -""" +"""Support for an exposed aREST RESTful API of a device.""" import logging from datetime import timedelta diff --git a/homeassistant/components/arest/switch.py b/homeassistant/components/arest/switch.py index ab445db10d8473..717acc2f33679c 100644 --- a/homeassistant/components/arest/switch.py +++ b/homeassistant/components/arest/switch.py @@ -1,9 +1,4 @@ -""" -Support for an exposed aREST RESTful API of a device. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.arest/ -""" +"""Support for an exposed aREST RESTful API of a device.""" import logging diff --git a/homeassistant/components/aruba/device_tracker.py b/homeassistant/components/aruba/device_tracker.py index 142842b12d2884..ed1fee25a6c837 100644 --- a/homeassistant/components/aruba/device_tracker.py +++ b/homeassistant/components/aruba/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Aruba Access Points. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.aruba/ -""" +"""Support for Aruba Access Points.""" import logging import re diff --git a/homeassistant/components/arwn/sensor.py b/homeassistant/components/arwn/sensor.py index 95825f4ca138d1..aef43c4b401556 100644 --- a/homeassistant/components/arwn/sensor.py +++ b/homeassistant/components/arwn/sensor.py @@ -1,9 +1,4 @@ -""" -Support for collecting data from the ARWN project. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.arwn/ -""" +"""Support for collecting data from the ARWN project.""" import json import logging diff --git a/homeassistant/components/asuswrt/device_tracker.py b/homeassistant/components/asuswrt/device_tracker.py index f5c6dd4a42a122..d115e640ffa863 100644 --- a/homeassistant/components/asuswrt/device_tracker.py +++ b/homeassistant/components/asuswrt/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for ASUSWRT routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.asuswrt/ -""" +"""Support for ASUSWRT routers.""" import logging from homeassistant.components.device_tracker import DeviceScanner diff --git a/homeassistant/components/asuswrt/sensor.py b/homeassistant/components/asuswrt/sensor.py index 53d232862c6b72..ac80a447e28716 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -1,9 +1,4 @@ -""" -Asuswrt status sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.asuswrt/ -""" +"""Asuswrt status sensors.""" import logging from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/aurora/binary_sensor.py b/homeassistant/components/aurora/binary_sensor.py index cfd683346ff7e7..58546382a50253 100644 --- a/homeassistant/components/aurora/binary_sensor.py +++ b/homeassistant/components/aurora/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for aurora forecast data sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.aurora/ -""" +"""Support for aurora forecast data sensor.""" from datetime import timedelta import logging diff --git a/homeassistant/components/automatic/device_tracker.py b/homeassistant/components/automatic/device_tracker.py index 9f20eb6d493b54..8abd81e63bea6d 100644 --- a/homeassistant/components/automatic/device_tracker.py +++ b/homeassistant/components/automatic/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for the Automatic platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.automatic/ -""" +"""Support for the Automatic platform.""" import asyncio from datetime import timedelta import json diff --git a/homeassistant/components/avion/light.py b/homeassistant/components/avion/light.py index 617198b2c8cdae..65172025b56b51 100644 --- a/homeassistant/components/avion/light.py +++ b/homeassistant/components/avion/light.py @@ -1,9 +1,4 @@ -""" -Support for Avion dimmers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.avion/ -""" +"""Support for Avion dimmers.""" import importlib import logging import time diff --git a/homeassistant/components/awair/sensor.py b/homeassistant/components/awair/sensor.py index 9a45cb66a86a41..5b199538e68079 100644 --- a/homeassistant/components/awair/sensor.py +++ b/homeassistant/components/awair/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Awair indoor air quality monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.awair/ -""" +"""Support for the Awair indoor air quality monitor.""" from datetime import timedelta import logging diff --git a/homeassistant/components/aws_lambda/notify.py b/homeassistant/components/aws_lambda/notify.py index d7ebb40d19aa62..e5fed20d9976c5 100644 --- a/homeassistant/components/aws_lambda/notify.py +++ b/homeassistant/components/aws_lambda/notify.py @@ -1,9 +1,4 @@ -""" -AWS Lambda platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.aws_lambda/ -""" +"""AWS Lambda platform for notify component.""" import base64 import json import logging diff --git a/homeassistant/components/aws_sns/notify.py b/homeassistant/components/aws_sns/notify.py index 09018562cb8dcf..daac710d40ac4d 100644 --- a/homeassistant/components/aws_sns/notify.py +++ b/homeassistant/components/aws_sns/notify.py @@ -1,9 +1,4 @@ -""" -AWS SNS platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.aws_sns/ -""" +"""AWS SNS platform for notify component.""" import json import logging diff --git a/homeassistant/components/aws_sqs/notify.py b/homeassistant/components/aws_sqs/notify.py index eff9018bae9a4f..4c4c831482b813 100644 --- a/homeassistant/components/aws_sqs/notify.py +++ b/homeassistant/components/aws_sqs/notify.py @@ -1,9 +1,4 @@ -""" -AWS SQS platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.aws_sqs/ -""" +"""AWS SQS platform for notify component.""" import json import logging diff --git a/homeassistant/components/baidu/tts.py b/homeassistant/components/baidu/tts.py index 07b69d41dfd624..fbe27591ef535c 100644 --- a/homeassistant/components/baidu/tts.py +++ b/homeassistant/components/baidu/tts.py @@ -1,10 +1,4 @@ -""" -Support for Baidu speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.baidu/ -""" - +"""Support for Baidu speech service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/bayesian/binary_sensor.py b/homeassistant/components/bayesian/binary_sensor.py index 97889ea749723c..6b2395ef6d2a45 100644 --- a/homeassistant/components/bayesian/binary_sensor.py +++ b/homeassistant/components/bayesian/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Use Bayesian Inference to trigger a binary sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.bayesian/ -""" +"""Use Bayesian Inference to trigger a binary sensor.""" from collections import OrderedDict import voluptuous as vol diff --git a/homeassistant/components/bbox/device_tracker.py b/homeassistant/components/bbox/device_tracker.py index f59c922577b011..badbcdc8a0bf56 100644 --- a/homeassistant/components/bbox/device_tracker.py +++ b/homeassistant/components/bbox/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for French FAI Bouygues Bbox routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.bbox/ -""" +"""Support for French FAI Bouygues Bbox routers.""" from collections import namedtuple from datetime import timedelta import logging diff --git a/homeassistant/components/bbox/sensor.py b/homeassistant/components/bbox/sensor.py index c81160dc2ae847..5b3c31d1ddf241 100644 --- a/homeassistant/components/bbox/sensor.py +++ b/homeassistant/components/bbox/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Bbox Bouygues Modem Router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bbox/ -""" +"""Support for Bbox Bouygues Modem Router.""" import logging from datetime import timedelta diff --git a/homeassistant/components/bh1750/sensor.py b/homeassistant/components/bh1750/sensor.py index 592a6acbe58020..e30eededa51b28 100644 --- a/homeassistant/components/bh1750/sensor.py +++ b/homeassistant/components/bh1750/sensor.py @@ -1,9 +1,4 @@ -""" -Support for BH1750 light sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bh1750/ -""" +"""Support for BH1750 light sensor.""" from functools import partial import logging diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index 9972e4dca3b4c3..029ed8faa6bbee 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with binary sensors. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/binary_sensor/ -""" +"""Component to interface with binary sensors.""" from datetime import timedelta import logging diff --git a/homeassistant/components/bitcoin/sensor.py b/homeassistant/components/bitcoin/sensor.py index e654f29f42a56a..3bc14637a87631 100644 --- a/homeassistant/components/bitcoin/sensor.py +++ b/homeassistant/components/bitcoin/sensor.py @@ -1,9 +1,4 @@ -""" -Bitcoin information service that uses blockchain.info. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bitcoin/ -""" +"""Bitcoin information service that uses blockchain.info.""" import logging from datetime import timedelta diff --git a/homeassistant/components/blackbird/media_player.py b/homeassistant/components/blackbird/media_player.py index 2daa2656e83063..c66bc412160f0e 100644 --- a/homeassistant/components/blackbird/media_player.py +++ b/homeassistant/components/blackbird/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with Monoprice Blackbird 4k 8x8 HDBaseT Matrix. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.blackbird -""" +"""Support for interfacing with Monoprice Blackbird 4k 8x8 HDBaseT Matrix.""" import logging import socket diff --git a/homeassistant/components/blinksticklight/light.py b/homeassistant/components/blinksticklight/light.py index e145005a5a7c7d..0d4c7b736f3d3d 100644 --- a/homeassistant/components/blinksticklight/light.py +++ b/homeassistant/components/blinksticklight/light.py @@ -1,9 +1,4 @@ -""" -Support for Blinkstick lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.blinksticklight/ -""" +"""Support for Blinkstick lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/blinkt/light.py b/homeassistant/components/blinkt/light.py index 0704881bff9663..57d19172614c9f 100644 --- a/homeassistant/components/blinkt/light.py +++ b/homeassistant/components/blinkt/light.py @@ -1,9 +1,4 @@ -""" -Support for Blinkt! lights on Raspberry Pi. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.blinkt/ -""" +"""Support for Blinkt! lights on Raspberry Pi.""" import importlib import logging diff --git a/homeassistant/components/blockchain/sensor.py b/homeassistant/components/blockchain/sensor.py index 241c98d23284f7..def1dc3309f9a9 100644 --- a/homeassistant/components/blockchain/sensor.py +++ b/homeassistant/components/blockchain/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Blockchain.info sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.blockchain/ -""" +"""Support for Blockchain.info sensors.""" import logging from datetime import timedelta diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index b25916c7f66d9a..c4cd3572e75c3b 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Bluesound devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.bluesound/ -""" +"""Support for Bluesound devices.""" import asyncio from asyncio.futures import CancelledError from datetime import timedelta diff --git a/homeassistant/components/bluetooth_le_tracker/device_tracker.py b/homeassistant/components/bluetooth_le_tracker/device_tracker.py index 825ef04ccc5b35..dfb5fa073b93de 100644 --- a/homeassistant/components/bluetooth_le_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_le_tracker/device_tracker.py @@ -1,9 +1,4 @@ -""" -Tracking for bluetooth low energy devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.bluetooth_le_tracker/ -""" +"""Tracking for bluetooth low energy devices.""" import logging from homeassistant.helpers.event import track_point_in_utc_time diff --git a/homeassistant/components/bluetooth_tracker/device_tracker.py b/homeassistant/components/bluetooth_tracker/device_tracker.py index 89f3b95ac1b7e8..3a4aa8880012d1 100644 --- a/homeassistant/components/bluetooth_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_tracker/device_tracker.py @@ -1,9 +1,4 @@ -""" -Tracking for bluetooth devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.bluetooth_tracker/ -""" +"""Tracking for bluetooth devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/bme280/sensor.py b/homeassistant/components/bme280/sensor.py index a6b773040ef046..73982ecc628bb0 100644 --- a/homeassistant/components/bme280/sensor.py +++ b/homeassistant/components/bme280/sensor.py @@ -1,9 +1,4 @@ -""" -Support for BME280 temperature, humidity and pressure sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bme280/ -""" +"""Support for BME280 temperature, humidity and pressure sensor.""" from datetime import timedelta from functools import partial import logging diff --git a/homeassistant/components/bme680/sensor.py b/homeassistant/components/bme680/sensor.py index 8d620e459d0388..8f515cc469a265 100644 --- a/homeassistant/components/bme680/sensor.py +++ b/homeassistant/components/bme680/sensor.py @@ -1,12 +1,4 @@ -""" -Support for BME680 Sensor over SMBus. - -Temperature, humidity, pressure and volatile gas support. -Air Quality calculation based on humidity and volatile gas. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bme680/ -""" +"""Support for BME680 Sensor over SMBus.""" import importlib import logging diff --git a/homeassistant/components/bom/sensor.py b/homeassistant/components/bom/sensor.py index 62a3706034ad38..4c96315ec1f793 100644 --- a/homeassistant/components/bom/sensor.py +++ b/homeassistant/components/bom/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Australian BOM (Bureau of Meteorology) weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.bom/ -""" +"""Support for Australian BOM (Bureau of Meteorology) weather service.""" import datetime import ftplib import gzip diff --git a/homeassistant/components/braviatv/media_player.py b/homeassistant/components/braviatv/media_player.py index 7efb7abd569d7c..45fdb63a4a9b45 100644 --- a/homeassistant/components/braviatv/media_player.py +++ b/homeassistant/components/braviatv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with a Sony Bravia TV. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.braviatv/ -""" +"""Support for interface with a Sony Bravia TV.""" import logging import re diff --git a/homeassistant/components/broadlink/sensor.py b/homeassistant/components/broadlink/sensor.py index 5720201b3f2ea8..60f1ed5c6bc858 100644 --- a/homeassistant/components/broadlink/sensor.py +++ b/homeassistant/components/broadlink/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Broadlink RM2 Pro (only temperature) and A1 devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.broadlink/ -""" +"""Support for the Broadlink RM2 Pro (only temperature) and A1 devices.""" from datetime import timedelta import binascii import logging diff --git a/homeassistant/components/broadlink/switch.py b/homeassistant/components/broadlink/switch.py index 2237a0a2977880..8695f70786c6f2 100644 --- a/homeassistant/components/broadlink/switch.py +++ b/homeassistant/components/broadlink/switch.py @@ -1,9 +1,4 @@ -""" -Support for Broadlink RM devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.broadlink/ -""" +"""Support for Broadlink RM devices.""" import asyncio from base64 import b64decode, b64encode import binascii diff --git a/homeassistant/components/brottsplatskartan/sensor.py b/homeassistant/components/brottsplatskartan/sensor.py index c308f2eac53d63..f990dd1aba1630 100644 --- a/homeassistant/components/brottsplatskartan/sensor.py +++ b/homeassistant/components/brottsplatskartan/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor platform for Brottsplatskartan information. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.brottsplatskartan/ -""" +"""Sensor platform for Brottsplatskartan information.""" from collections import defaultdict from datetime import timedelta import logging diff --git a/homeassistant/components/brunt/cover.py b/homeassistant/components/brunt/cover.py index 746f3840a01ff8..dc17cebcec2fd6 100644 --- a/homeassistant/components/brunt/cover.py +++ b/homeassistant/components/brunt/cover.py @@ -1,9 +1,4 @@ -""" -Support for Brunt Blind Engine covers. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/cover.brunt -""" +"""Support for Brunt Blind Engine covers.""" import logging diff --git a/homeassistant/components/bt_home_hub_5/device_tracker.py b/homeassistant/components/bt_home_hub_5/device_tracker.py index 21c41df3a1d2ba..61853c0af89f0b 100644 --- a/homeassistant/components/bt_home_hub_5/device_tracker.py +++ b/homeassistant/components/bt_home_hub_5/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for BT Home Hub 5. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.bt_home_hub_5/ -""" +"""Support for BT Home Hub 5.""" import logging import voluptuous as vol diff --git a/homeassistant/components/bt_smarthub/device_tracker.py b/homeassistant/components/bt_smarthub/device_tracker.py index 821182ec1036f6..5820feda567903 100644 --- a/homeassistant/components/bt_smarthub/device_tracker.py +++ b/homeassistant/components/bt_smarthub/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for BT Smart Hub (Sometimes referred to as BT Home Hub 6). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.bt_smarthub/ -""" +"""Support for BT Smart Hub (Sometimes referred to as BT Home Hub 6).""" import logging import voluptuous as vol diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index d144d84cbf81e6..754873fa2c914f 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Buienradar.nl weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.buienradar/ -""" +"""Support for Buienradar.nl weather service.""" import asyncio from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index cb8874a817c5cc..65cb20811b880b 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -1,9 +1,4 @@ -""" -Support for WebDav Calendar. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/calendar.caldav/ -""" +"""Support for WebDav Calendar.""" from datetime import datetime, timedelta import logging import re diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index e453cdfd1a166a..10739a1c7bb55c 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with cameras. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/camera/ -""" +"""Component to interface with cameras.""" import asyncio import base64 import collections diff --git a/homeassistant/components/canary/alarm_control_panel.py b/homeassistant/components/canary/alarm_control_panel.py index 617942246667db..faa7d819a2e91f 100644 --- a/homeassistant/components/canary/alarm_control_panel.py +++ b/homeassistant/components/canary/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for Canary alarm. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.canary/ -""" +"""Support for Canary alarm.""" import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index c3a3af32450e38..fc740a46f628d9 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -1,9 +1,4 @@ -""" -Support for Canary camera. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.canary/ -""" +"""Support for Canary camera.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/canary/sensor.py b/homeassistant/components/canary/sensor.py index d24c00c926648d..fb3aaf78b0a487 100644 --- a/homeassistant/components/canary/sensor.py +++ b/homeassistant/components/canary/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Canary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.canary/ -""" +"""Support for Canary sensors.""" from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/cert_expiry/sensor.py b/homeassistant/components/cert_expiry/sensor.py index a04a631f2e9cc3..54ba378f91cc89 100644 --- a/homeassistant/components/cert_expiry/sensor.py +++ b/homeassistant/components/cert_expiry/sensor.py @@ -1,9 +1,4 @@ -""" -Counter for the days until an HTTPS (TLS) certificate will expire. - -For more details about this sensor please refer to the documentation at -https://home-assistant.io/components/sensor.cert_expiry/ -""" +"""Counter for the days until an HTTPS (TLS) certificate will expire.""" import logging import socket import ssl diff --git a/homeassistant/components/channels/media_player.py b/homeassistant/components/channels/media_player.py index 2f7b169601c9ee..afe29ae079f9ac 100644 --- a/homeassistant/components/channels/media_player.py +++ b/homeassistant/components/channels/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with an instance of Channels (https://getchannels.com). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.channels/ -""" +"""Support for interfacing with an instance of getchannels.com.""" import logging import voluptuous as vol diff --git a/homeassistant/components/cisco_ios/device_tracker.py b/homeassistant/components/cisco_ios/device_tracker.py index 1afea2c1607f73..d5a64626e89924 100644 --- a/homeassistant/components/cisco_ios/device_tracker.py +++ b/homeassistant/components/cisco_ios/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Cisco IOS Routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.cisco_ios/ -""" +"""Support for Cisco IOS Routers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ciscospark/notify.py b/homeassistant/components/ciscospark/notify.py index 1eeb9b51f28486..2eccb233a3cdd6 100644 --- a/homeassistant/components/ciscospark/notify.py +++ b/homeassistant/components/ciscospark/notify.py @@ -1,9 +1,4 @@ -""" -Cisco Spark platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.ciscospark/ -""" +"""Cisco Spark platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/citybikes/sensor.py b/homeassistant/components/citybikes/sensor.py index 12c475e62ff212..bcf6fb923f9b52 100644 --- a/homeassistant/components/citybikes/sensor.py +++ b/homeassistant/components/citybikes/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for the CityBikes data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.citybikes/ -""" +"""Sensor for the CityBikes data.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/clementine/media_player.py b/homeassistant/components/clementine/media_player.py index 24df7c24611a5e..65c6be19845faa 100644 --- a/homeassistant/components/clementine/media_player.py +++ b/homeassistant/components/clementine/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Clementine Music Player as media player. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.clementine/ -""" +"""Support for Clementine Music Player as media player.""" from datetime import timedelta import logging import time diff --git a/homeassistant/components/clickatell/notify.py b/homeassistant/components/clickatell/notify.py index e473e54a3b7876..b512a288ed5698 100644 --- a/homeassistant/components/clickatell/notify.py +++ b/homeassistant/components/clickatell/notify.py @@ -1,9 +1,4 @@ -""" -Clickatell platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.clickatell/ -""" +"""Clickatell platform for notify component.""" import logging import requests diff --git a/homeassistant/components/clicksend/notify.py b/homeassistant/components/clicksend/notify.py index 3b2cdb7496db36..111ae63601faa8 100644 --- a/homeassistant/components/clicksend/notify.py +++ b/homeassistant/components/clicksend/notify.py @@ -1,9 +1,4 @@ -""" -Clicksend platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.clicksend/ -""" +"""Clicksend platform for notify component.""" import json import logging diff --git a/homeassistant/components/clicksend_tts/notify.py b/homeassistant/components/clicksend_tts/notify.py index 93e5126bbab4cd..feb4481fb5660d 100644 --- a/homeassistant/components/clicksend_tts/notify.py +++ b/homeassistant/components/clicksend_tts/notify.py @@ -1,11 +1,4 @@ -""" -clicksend_tts platform for notify component. - -This platform sends text to speech audio messages through clicksend - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.clicksend_tts/ -""" +"""clicksend_tts platform for notify component.""" import json import logging diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 0283359b1f234a..18b56049f83ba8 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -1,9 +1,4 @@ -""" -Provides functionality to interact with climate devices. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/climate/ -""" +"""Provides functionality to interact with climate devices.""" from datetime import timedelta import logging import functools as ft diff --git a/homeassistant/components/cmus/media_player.py b/homeassistant/components/cmus/media_player.py index 20b292749b4ec0..e5134508feaaaa 100644 --- a/homeassistant/components/cmus/media_player.py +++ b/homeassistant/components/cmus/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interacting with and controlling the cmus music player. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.cmus/ -""" +"""Support for interacting with and controlling the cmus music player.""" import logging import voluptuous as vol diff --git a/homeassistant/components/co2signal/sensor.py b/homeassistant/components/co2signal/sensor.py index 7b4cd67bd70700..b9ae5e26ebef20 100644 --- a/homeassistant/components/co2signal/sensor.py +++ b/homeassistant/components/co2signal/sensor.py @@ -1,8 +1,4 @@ -""" -Support for the CO2signal platform. - -For more details about this platform, please refer to the documentation -""" +"""Support for the CO2signal platform.""" import logging import voluptuous as vol diff --git a/homeassistant/components/coinbase/sensor.py b/homeassistant/components/coinbase/sensor.py index 54af94944d6c20..2483d46b38a095 100644 --- a/homeassistant/components/coinbase/sensor.py +++ b/homeassistant/components/coinbase/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Coinbase sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.coinbase/ -""" +"""Support for Coinbase sensors.""" from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.helpers.entity import Entity diff --git a/homeassistant/components/coinmarketcap/sensor.py b/homeassistant/components/coinmarketcap/sensor.py index 9143405a553a5a..a39f11b5352a0a 100644 --- a/homeassistant/components/coinmarketcap/sensor.py +++ b/homeassistant/components/coinmarketcap/sensor.py @@ -1,9 +1,4 @@ -""" -Details about crypto currencies from CoinMarketCap. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.coinmarketcap/ -""" +"""Details about crypto currencies from CoinMarketCap.""" import logging from datetime import timedelta from urllib.error import HTTPError diff --git a/homeassistant/components/comed_hourly_pricing/sensor.py b/homeassistant/components/comed_hourly_pricing/sensor.py index 1771fd0f1a3dcc..384aadd8bf4725 100644 --- a/homeassistant/components/comed_hourly_pricing/sensor.py +++ b/homeassistant/components/comed_hourly_pricing/sensor.py @@ -1,9 +1,4 @@ -""" -Support for ComEd Hourly Pricing data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.comed_hourly_pricing/ -""" +"""Support for ComEd Hourly Pricing data.""" import asyncio from datetime import timedelta import json diff --git a/homeassistant/components/command_line/binary_sensor.py b/homeassistant/components/command_line/binary_sensor.py index 21ee1312e7a488..860367d8091881 100644 --- a/homeassistant/components/command_line/binary_sensor.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for custom shell commands to retrieve values. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.command_line/ -""" +"""Support for custom shell commands to retrieve values.""" from datetime import timedelta import logging diff --git a/homeassistant/components/command_line/cover.py b/homeassistant/components/command_line/cover.py index 4f4fca1b27a965..7f3c52799052c4 100644 --- a/homeassistant/components/command_line/cover.py +++ b/homeassistant/components/command_line/cover.py @@ -1,9 +1,4 @@ -""" -Support for command line covers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover.command_line/ -""" +"""Support for command line covers.""" import logging import subprocess diff --git a/homeassistant/components/command_line/notify.py b/homeassistant/components/command_line/notify.py index 7ea5a6d8880bd3..941be72aa81693 100644 --- a/homeassistant/components/command_line/notify.py +++ b/homeassistant/components/command_line/notify.py @@ -1,9 +1,4 @@ -""" -Support for command line notification services. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.command_line/ -""" +"""Support for command line notification services.""" import logging import subprocess diff --git a/homeassistant/components/command_line/sensor.py b/homeassistant/components/command_line/sensor.py index e1d151410b1a93..16d39762879227 100644 --- a/homeassistant/components/command_line/sensor.py +++ b/homeassistant/components/command_line/sensor.py @@ -1,9 +1,4 @@ -""" -Allows to configure custom shell commands to turn a value for a sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.command_line/ -""" +"""Allows to configure custom shell commands to turn a value for a sensor.""" import collections from datetime import timedelta import json diff --git a/homeassistant/components/command_line/switch.py b/homeassistant/components/command_line/switch.py index 4edbd79ee0cfa1..8d97198ad66350 100644 --- a/homeassistant/components/command_line/switch.py +++ b/homeassistant/components/command_line/switch.py @@ -1,9 +1,4 @@ -""" -Support for custom shell commands to turn a switch on/off. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.command_line/ -""" +"""Support for custom shell commands to turn a switch on/off.""" import logging import subprocess diff --git a/homeassistant/components/concord232/alarm_control_panel.py b/homeassistant/components/concord232/alarm_control_panel.py index 155d6b6ae49921..4821e589b13ed1 100644 --- a/homeassistant/components/concord232/alarm_control_panel.py +++ b/homeassistant/components/concord232/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for Concord232 alarm control panels. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.concord232/ -""" +"""Support for Concord232 alarm control panels.""" import datetime import logging diff --git a/homeassistant/components/concord232/binary_sensor.py b/homeassistant/components/concord232/binary_sensor.py index 26f35d60305825..5aff0f09983c38 100644 --- a/homeassistant/components/concord232/binary_sensor.py +++ b/homeassistant/components/concord232/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for exposing Concord232 elements as sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.concord232/ -""" +"""Support for exposing Concord232 elements as sensors.""" import datetime import logging diff --git a/homeassistant/components/coolmaster/climate.py b/homeassistant/components/coolmaster/climate.py index fd00c9f22c4685..77bb9a6b213d29 100644 --- a/homeassistant/components/coolmaster/climate.py +++ b/homeassistant/components/coolmaster/climate.py @@ -1,9 +1,4 @@ -""" -CoolMasterNet platform that offers control of CoolMasteNet Climate Devices. - -For more details about this platform, please refer to the documentation -https://www.home-assistant.io/components/climate.coolmaster/ -""" +"""CoolMasterNet platform to control of CoolMasteNet Climate Devices.""" import logging diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 8b4031f09edd36..9bb1aacfaf13ea 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -1,9 +1,4 @@ -""" -Support for Cover devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover/ -""" +"""Support for Cover devices.""" from datetime import timedelta import functools as ft import logging diff --git a/homeassistant/components/cppm_tracker/device_tracker.py b/homeassistant/components/cppm_tracker/device_tracker.py index 2ca0ebf62e5bc8..31d8122692a39e 100755 --- a/homeassistant/components/cppm_tracker/device_tracker.py +++ b/homeassistant/components/cppm_tracker/device_tracker.py @@ -1,8 +1,4 @@ -""" -Support for ClearPass Policy Manager. - -Allows tracking devices with CPPM. -""" +"""Support for ClearPass Policy Manager.""" import logging from datetime import timedelta diff --git a/homeassistant/components/cups/sensor.py b/homeassistant/components/cups/sensor.py index 99dadcfe596062..97f894aed86981 100644 --- a/homeassistant/components/cups/sensor.py +++ b/homeassistant/components/cups/sensor.py @@ -1,9 +1,4 @@ -""" -Details about printers which are connected to CUPS. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.cups/ -""" +"""Details about printers which are connected to CUPS.""" import importlib import logging from datetime import timedelta diff --git a/homeassistant/components/currencylayer/sensor.py b/homeassistant/components/currencylayer/sensor.py index 9b7186e8e09524..bedd5f079ce43a 100644 --- a/homeassistant/components/currencylayer/sensor.py +++ b/homeassistant/components/currencylayer/sensor.py @@ -1,9 +1,4 @@ -""" -Support for currencylayer.com exchange rates service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.currencylayer/ -""" +"""Support for currencylayer.com exchange rates service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index 540568b5785dfc..70b07ee773f65c 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Dark Sky weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.darksky/ -""" +"""Support for Dark Sky weather service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/ddwrt/device_tracker.py b/homeassistant/components/ddwrt/device_tracker.py index cf8c8e1779b282..a97fe340f927ed 100644 --- a/homeassistant/components/ddwrt/device_tracker.py +++ b/homeassistant/components/ddwrt/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for DD-WRT routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ddwrt/ -""" +"""Support for DD-WRT routers.""" import logging import re diff --git a/homeassistant/components/decora/light.py b/homeassistant/components/decora/light.py index 7c3274cf83bf9c..fc8b2859c0712d 100644 --- a/homeassistant/components/decora/light.py +++ b/homeassistant/components/decora/light.py @@ -1,9 +1,4 @@ -""" -Support for Decora dimmers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.decora/ -""" +"""Support for Decora dimmers.""" import importlib import logging from functools import wraps diff --git a/homeassistant/components/decora_wifi/light.py b/homeassistant/components/decora_wifi/light.py index b9c575dbd5a1b2..b7be6bffb0151a 100644 --- a/homeassistant/components/decora_wifi/light.py +++ b/homeassistant/components/decora_wifi/light.py @@ -1,12 +1,4 @@ -""" -Interfaces with the myLeviton API for Decora Smart WiFi products. - -See: -http://www.leviton.com/en/products/lighting-controls/decora-smart-with-wifi - -Uses Leviton's cloud services API for cloud-to-cloud integration. - -""" +"""Interfaces with the myLeviton API for Decora Smart WiFi products.""" import logging diff --git a/homeassistant/components/deluge/sensor.py b/homeassistant/components/deluge/sensor.py index f56b3ac4b97149..32b1c16a47c783 100644 --- a/homeassistant/components/deluge/sensor.py +++ b/homeassistant/components/deluge/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring the Deluge BitTorrent client API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.deluge/ -""" +"""Support for monitoring the Deluge BitTorrent client API.""" import logging import voluptuous as vol diff --git a/homeassistant/components/deluge/switch.py b/homeassistant/components/deluge/switch.py index 0ece742aa03f2b..d7c60bd96e2932 100644 --- a/homeassistant/components/deluge/switch.py +++ b/homeassistant/components/deluge/switch.py @@ -1,9 +1,4 @@ -""" -Support for setting the Deluge BitTorrent client in Pause. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.deluge/ -""" +"""Support for setting the Deluge BitTorrent client in Pause.""" import logging import voluptuous as vol diff --git a/homeassistant/components/demo/air_quality.py b/homeassistant/components/demo/air_quality.py index b2b9c10574f1d6..77e5c0b2b1a1ff 100644 --- a/homeassistant/components/demo/air_quality.py +++ b/homeassistant/components/demo/air_quality.py @@ -1,9 +1,4 @@ -""" -Demo platform that offers fake air quality data. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that offers fake air quality data.""" from homeassistant.components.air_quality import AirQualityEntity diff --git a/homeassistant/components/demo/alarm_control_panel.py b/homeassistant/components/demo/alarm_control_panel.py index 4d317f52daacd0..3cf5aaca57e797 100644 --- a/homeassistant/components/demo/alarm_control_panel.py +++ b/homeassistant/components/demo/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Demo platform that has two fake alarm control panels. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has two fake alarm control panels.""" import datetime from homeassistant.components.manual.alarm_control_panel import ManualAlarm from homeassistant.const import ( diff --git a/homeassistant/components/demo/binary_sensor.py b/homeassistant/components/demo/binary_sensor.py index d656b79e8ed2e6..437497e4faccab 100644 --- a/homeassistant/components/demo/binary_sensor.py +++ b/homeassistant/components/demo/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Demo platform that has two fake binary sensors. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has two fake binary sensors.""" from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/demo/calendar.py b/homeassistant/components/demo/calendar.py index 720b4cc51809be..6096f8247c49fc 100644 --- a/homeassistant/components/demo/calendar.py +++ b/homeassistant/components/demo/calendar.py @@ -1,9 +1,4 @@ -""" -Demo platform that has two fake binary sensors. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has two fake binary sensors.""" import copy from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME diff --git a/homeassistant/components/demo/camera.py b/homeassistant/components/demo/camera.py index 34a0894ac60ba8..95c7df58200867 100644 --- a/homeassistant/components/demo/camera.py +++ b/homeassistant/components/demo/camera.py @@ -1,9 +1,4 @@ -""" -Demo camera platform that has a fake camera. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo camera platform that has a fake camera.""" import logging import os diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py index b1dd1b0ba456cc..70eed0c3616015 100644 --- a/homeassistant/components/demo/climate.py +++ b/homeassistant/components/demo/climate.py @@ -1,9 +1,4 @@ -""" -Demo platform that offers a fake climate device. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that offers a fake climate device.""" from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.components.climate import ClimateDevice diff --git a/homeassistant/components/demo/cover.py b/homeassistant/components/demo/cover.py index ddcf07fd5e5127..aa2931a987a30f 100644 --- a/homeassistant/components/demo/cover.py +++ b/homeassistant/components/demo/cover.py @@ -1,9 +1,4 @@ -""" -Demo platform for the cover component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform for the cover component.""" from homeassistant.helpers.event import track_utc_time_change from homeassistant.components.cover import ( diff --git a/homeassistant/components/demo/device_tracker.py b/homeassistant/components/demo/device_tracker.py index 608fc560cf97dd..ff038d7009e047 100644 --- a/homeassistant/components/demo/device_tracker.py +++ b/homeassistant/components/demo/device_tracker.py @@ -1,9 +1,4 @@ -""" -Demo platform for the Device tracker component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform for the Device tracker component.""" import random from homeassistant.components.device_tracker import DOMAIN diff --git a/homeassistant/components/demo/fan.py b/homeassistant/components/demo/fan.py index 53729795f71bf1..4710bbecfe1f46 100644 --- a/homeassistant/components/demo/fan.py +++ b/homeassistant/components/demo/fan.py @@ -1,9 +1,4 @@ -""" -Demo fan platform that has a fake fan. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo fan platform that has a fake fan.""" from homeassistant.const import STATE_OFF from homeassistant.components.fan import ( diff --git a/homeassistant/components/demo/image_processing.py b/homeassistant/components/demo/image_processing.py index 71ec2dccbc600c..acb97e4ebd6132 100644 --- a/homeassistant/components/demo/image_processing.py +++ b/homeassistant/components/demo/image_processing.py @@ -1,9 +1,4 @@ -""" -Support for the demo image processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/demo/ -""" +"""Support for the demo image processing.""" from homeassistant.components.image_processing import ( ImageProcessingFaceEntity, ATTR_CONFIDENCE, ATTR_NAME, ATTR_AGE, ATTR_GENDER diff --git a/homeassistant/components/demo/light.py b/homeassistant/components/demo/light.py index a5b22108e8130a..285866c6eb8c9b 100644 --- a/homeassistant/components/demo/light.py +++ b/homeassistant/components/demo/light.py @@ -1,9 +1,4 @@ -""" -Demo light platform that implements lights. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo light platform that implements lights.""" import random from homeassistant.components.light import ( diff --git a/homeassistant/components/demo/lock.py b/homeassistant/components/demo/lock.py index 03935c4f603bcc..cd15a434138050 100644 --- a/homeassistant/components/demo/lock.py +++ b/homeassistant/components/demo/lock.py @@ -1,9 +1,4 @@ -""" -Demo lock platform that has two fake locks. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo lock platform that has two fake locks.""" from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED from homeassistant.components.lock import SUPPORT_OPEN, LockDevice diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py index 33d2b98d225ece..5ad13b4e9953ae 100644 --- a/homeassistant/components/demo/media_player.py +++ b/homeassistant/components/demo/media_player.py @@ -1,9 +1,4 @@ -""" -Demo implementation of the media player. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo implementation of the media player.""" from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING import homeassistant.util.dt as dt_util diff --git a/homeassistant/components/demo/notify.py b/homeassistant/components/demo/notify.py index 5b8e1f1688f099..92aaea6882dba5 100644 --- a/homeassistant/components/demo/notify.py +++ b/homeassistant/components/demo/notify.py @@ -1,9 +1,4 @@ -""" -Demo notification service. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo notification service.""" from homeassistant.components.notify import BaseNotificationService EVENT_NOTIFY = "notify" diff --git a/homeassistant/components/demo/remote.py b/homeassistant/components/demo/remote.py index f44061ac8f9eff..b28330fdc67f6f 100644 --- a/homeassistant/components/demo/remote.py +++ b/homeassistant/components/demo/remote.py @@ -1,9 +1,4 @@ -""" -Demo platform that has two fake remotes. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has two fake remotes.""" from homeassistant.components.remote import RemoteDevice from homeassistant.const import DEVICE_DEFAULT_NAME diff --git a/homeassistant/components/demo/sensor.py b/homeassistant/components/demo/sensor.py index 7921181b742581..ea35c729517f7a 100644 --- a/homeassistant/components/demo/sensor.py +++ b/homeassistant/components/demo/sensor.py @@ -1,9 +1,4 @@ -""" -Demo platform that has a couple of fake sensors. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has a couple of fake sensors.""" from homeassistant.const import ( ATTR_BATTERY_LEVEL, TEMP_CELSIUS, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE) diff --git a/homeassistant/components/demo/switch.py b/homeassistant/components/demo/switch.py index 0ac2011a6dc486..04a55a591b77a3 100644 --- a/homeassistant/components/demo/switch.py +++ b/homeassistant/components/demo/switch.py @@ -1,9 +1,4 @@ -""" -Demo platform that has two fake switches. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform that has two fake switches.""" from homeassistant.components.switch import SwitchDevice from homeassistant.const import DEVICE_DEFAULT_NAME diff --git a/homeassistant/components/demo/tts.py b/homeassistant/components/demo/tts.py index 1498472ef9f29c..bf18bc1630f66c 100644 --- a/homeassistant/components/demo/tts.py +++ b/homeassistant/components/demo/tts.py @@ -1,9 +1,4 @@ -""" -Support for the demo speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/demo/ -""" +"""Support for the demo speech service.""" import os import voluptuous as vol diff --git a/homeassistant/components/demo/vacuum.py b/homeassistant/components/demo/vacuum.py index 5ec7030f56cf9d..dfb9c4e943e97a 100644 --- a/homeassistant/components/demo/vacuum.py +++ b/homeassistant/components/demo/vacuum.py @@ -1,9 +1,4 @@ -""" -Demo platform for the vacuum component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/demo/ -""" +"""Demo platform for the vacuum component.""" import logging from homeassistant.components.vacuum import ( diff --git a/homeassistant/components/denon/media_player.py b/homeassistant/components/denon/media_player.py index 3dc4e550d9b9dc..07f6fcc7f9c71b 100644 --- a/homeassistant/components/denon/media_player.py +++ b/homeassistant/components/denon/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Denon Network Receivers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.denon/ -""" +"""Support for Denon Network Receivers.""" import logging import telnetlib diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index 380484add53f50..0adafe4f472e2e 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Denon AVR receivers using their HTTP interface. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.denon/ -""" +"""Support for Denon AVR receivers using their HTTP interface.""" from collections import namedtuple import logging diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 1263811aae76d3..42d301721dad47 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -1,9 +1,4 @@ -""" -Provide functionality to keep track of devices. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/device_tracker/ -""" +"""Provide functionality to keep track of devices.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/dht/sensor.py b/homeassistant/components/dht/sensor.py index 04c084784c73f9..719c2525f0a985 100644 --- a/homeassistant/components/dht/sensor.py +++ b/homeassistant/components/dht/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Adafruit DHT temperature and humidity sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dht/ -""" +"""Support for Adafruit DHT temperature and humidity sensor.""" import logging from datetime import timedelta diff --git a/homeassistant/components/digitalloggers/switch.py b/homeassistant/components/digitalloggers/switch.py index 7bb2be19899425..89973cfad0c828 100644 --- a/homeassistant/components/digitalloggers/switch.py +++ b/homeassistant/components/digitalloggers/switch.py @@ -1,9 +1,4 @@ -""" -Support for Digital Loggers DIN III Relays. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.digitalloggers/ -""" +"""Support for Digital Loggers DIN III Relays.""" import logging from datetime import timedelta diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index 9c5a3bf07b82ba..3a30282bdf49be 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for the DirecTV receivers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.directv/ -""" +"""Support for the DirecTV receivers.""" import logging import requests import voluptuous as vol diff --git a/homeassistant/components/discogs/sensor.py b/homeassistant/components/discogs/sensor.py index 8cdc89a540eb67..f8d66688b4ff65 100644 --- a/homeassistant/components/discogs/sensor.py +++ b/homeassistant/components/discogs/sensor.py @@ -1,9 +1,4 @@ -""" -Show the amount of records in a user's Discogs collection. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.discogs/ -""" +"""Show the amount of records in a user's Discogs collection.""" from datetime import timedelta import logging import random diff --git a/homeassistant/components/discord/notify.py b/homeassistant/components/discord/notify.py index d73382d8bcf980..cb6fc8329c66b5 100644 --- a/homeassistant/components/discord/notify.py +++ b/homeassistant/components/discord/notify.py @@ -1,9 +1,4 @@ -""" -Discord platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.discord/ -""" +"""Discord platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/dlib_face_detect/image_processing.py b/homeassistant/components/dlib_face_detect/image_processing.py index fea756395e427c..49fbfadff7e779 100644 --- a/homeassistant/components/dlib_face_detect/image_processing.py +++ b/homeassistant/components/dlib_face_detect/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the Dlib face detect processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.dlib_face_detect/ -""" +"""Component that will help set the Dlib face detect processing.""" import logging import io diff --git a/homeassistant/components/dlib_face_identify/image_processing.py b/homeassistant/components/dlib_face_identify/image_processing.py index 6611fb0edfbf95..a3b91235125e43 100644 --- a/homeassistant/components/dlib_face_identify/image_processing.py +++ b/homeassistant/components/dlib_face_identify/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the Dlib face detect processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.dlib_face_identify/ -""" +"""Component that will help set the Dlib face detect processing.""" import logging import io diff --git a/homeassistant/components/dlink/switch.py b/homeassistant/components/dlink/switch.py index de584510008b56..812fd3882b311b 100644 --- a/homeassistant/components/dlink/switch.py +++ b/homeassistant/components/dlink/switch.py @@ -1,9 +1,4 @@ -""" -Support for D-Link W215 smart switch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.dlink/ -""" +"""Support for D-Link W215 smart switch.""" from datetime import timedelta import logging import urllib diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 71195d66c696d8..54c19f70ef324d 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -1,9 +1,4 @@ -""" -Support for DLNA DMR (Device Media Renderer). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.dlna_dmr/ -""" +"""Support for DLNA DMR (Device Media Renderer).""" import asyncio from datetime import datetime from datetime import timedelta diff --git a/homeassistant/components/dnsip/sensor.py b/homeassistant/components/dnsip/sensor.py index 48cf8debea6f78..13c9be7bb14fb5 100644 --- a/homeassistant/components/dnsip/sensor.py +++ b/homeassistant/components/dnsip/sensor.py @@ -1,9 +1,4 @@ -""" -Get your own public IP address or that of any host. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dnsip/ -""" +"""Get your own public IP address or that of any host.""" import logging from datetime import timedelta diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 6319a68b0c865f..74f6cb37fc2ebc 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Dutch Smart Meter (also known as Smartmeter or P1 port). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dsmr/ -""" +"""Support for Dutch Smart Meter (also known as Smartmeter or P1 port).""" import asyncio from datetime import timedelta from functools import partial diff --git a/homeassistant/components/dte_energy_bridge/sensor.py b/homeassistant/components/dte_energy_bridge/sensor.py index 5de2fc4a4eed5b..8610a1e7f7008c 100644 --- a/homeassistant/components/dte_energy_bridge/sensor.py +++ b/homeassistant/components/dte_energy_bridge/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring energy usage using the DTE energy bridge. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dte_energy_bridge/ -""" +"""Support for monitoring energy usage using the DTE energy bridge.""" import logging import voluptuous as vol diff --git a/homeassistant/components/duke_energy/sensor.py b/homeassistant/components/duke_energy/sensor.py index 41d3e5706de05d..9aada34841880a 100644 --- a/homeassistant/components/duke_energy/sensor.py +++ b/homeassistant/components/duke_energy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Duke Energy Gas and Electric meters. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.duke_energy/ -""" +"""Support for Duke Energy Gas and Electric meters.""" import logging import voluptuous as vol diff --git a/homeassistant/components/dunehd/media_player.py b/homeassistant/components/dunehd/media_player.py index 796aea86414dc0..70d96424ced97e 100644 --- a/homeassistant/components/dunehd/media_player.py +++ b/homeassistant/components/dunehd/media_player.py @@ -1,9 +1,4 @@ -""" -DuneHD implementation of the media player. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/media_player.dunehd/ -""" +"""DuneHD implementation of the media player.""" import voluptuous as vol from homeassistant.components.media_player import ( diff --git a/homeassistant/components/dyson/climate.py b/homeassistant/components/dyson/climate.py index a24d011623bcb1..a0c4c56d3188bb 100644 --- a/homeassistant/components/dyson/climate.py +++ b/homeassistant/components/dyson/climate.py @@ -1,9 +1,4 @@ -""" -Support for Dyson Pure Hot+Cool link fan. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.dyson/ -""" +"""Support for Dyson Pure Hot+Cool link fan.""" import logging from homeassistant.components.climate import ClimateDevice diff --git a/homeassistant/components/dyson/sensor.py b/homeassistant/components/dyson/sensor.py index abf06f15437375..2c7a71f5724847 100644 --- a/homeassistant/components/dyson/sensor.py +++ b/homeassistant/components/dyson/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Dyson Pure Cool Link Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dyson/ -""" +"""Support for Dyson Pure Cool Link Sensors.""" import logging from homeassistant.const import STATE_OFF, TEMP_CELSIUS diff --git a/homeassistant/components/dyson/vacuum.py b/homeassistant/components/dyson/vacuum.py index 72c7b95562f850..f1822b4043b461 100644 --- a/homeassistant/components/dyson/vacuum.py +++ b/homeassistant/components/dyson/vacuum.py @@ -1,9 +1,4 @@ -""" -Support for the Dyson 360 eye vacuum cleaner robot. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/vacuum.dyson/ -""" +"""Support for the Dyson 360 eye vacuum cleaner robot.""" import logging from homeassistant.components.vacuum import ( diff --git a/homeassistant/components/edimax/switch.py b/homeassistant/components/edimax/switch.py index 90ad3fff57fbf1..338e6ac932cc4b 100644 --- a/homeassistant/components/edimax/switch.py +++ b/homeassistant/components/edimax/switch.py @@ -1,9 +1,4 @@ -""" -Support for Edimax switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.edimax/ -""" +"""Support for Edimax switches.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ee_brightbox/device_tracker.py b/homeassistant/components/ee_brightbox/device_tracker.py index fc23abda1db356..46e4a3c3c24307 100644 --- a/homeassistant/components/ee_brightbox/device_tracker.py +++ b/homeassistant/components/ee_brightbox/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for EE Brightbox router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ee_brightbox/ -""" +"""Support for EE Brightbox router.""" import logging import voluptuous as vol diff --git a/homeassistant/components/efergy/sensor.py b/homeassistant/components/efergy/sensor.py index b3c40b4fa251b6..eb8912abe18a3b 100644 --- a/homeassistant/components/efergy/sensor.py +++ b/homeassistant/components/efergy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Efergy sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.efergy/ -""" +"""Support for Efergy sensors.""" import logging import requests diff --git a/homeassistant/components/eliqonline/sensor.py b/homeassistant/components/eliqonline/sensor.py index b03164a30d4abc..198ca327997808 100644 --- a/homeassistant/components/eliqonline/sensor.py +++ b/homeassistant/components/eliqonline/sensor.py @@ -1,9 +1,4 @@ -""" -Monitors home energy use for the ELIQ Online service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.eliqonline/ -""" +"""Monitors home energy use for the ELIQ Online service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/emby/media_player.py b/homeassistant/components/emby/media_player.py index b1259db913d975..8a94664f352cbe 100644 --- a/homeassistant/components/emby/media_player.py +++ b/homeassistant/components/emby/media_player.py @@ -1,9 +1,4 @@ -""" -Support to interface with the Emby API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.emby/ -""" +"""Support to interface with the Emby API.""" import logging import voluptuous as vol diff --git a/homeassistant/components/emoncms/sensor.py b/homeassistant/components/emoncms/sensor.py index 5d619878d98479..6e059e1a30f31c 100644 --- a/homeassistant/components/emoncms/sensor.py +++ b/homeassistant/components/emoncms/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring emoncms feeds. - -For more details about this component, please refer to the documentation -at https://home-assistant.io/components/sensor.emoncms/ -""" +"""Support for monitoring emoncms feeds.""" from datetime import timedelta import logging diff --git a/homeassistant/components/enphase_envoy/sensor.py b/homeassistant/components/enphase_envoy/sensor.py index 1bfee88d41c8b3..2b62732dc9106f 100644 --- a/homeassistant/components/enphase_envoy/sensor.py +++ b/homeassistant/components/enphase_envoy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Enphase Envoy solar energy monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.enphase_envoy/ -""" +"""Support for Enphase Envoy solar energy monitor.""" import logging import voluptuous as vol diff --git a/homeassistant/components/envirophat/sensor.py b/homeassistant/components/envirophat/sensor.py index 7683c06b69c53b..16cb79406a9b9d 100644 --- a/homeassistant/components/envirophat/sensor.py +++ b/homeassistant/components/envirophat/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Enviro pHAT sensors. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.envirophat -""" +"""Support for Enviro pHAT sensors.""" import importlib import logging from datetime import timedelta diff --git a/homeassistant/components/ephember/climate.py b/homeassistant/components/ephember/climate.py index 220c073ef800b8..3052dd911ee5de 100644 --- a/homeassistant/components/ephember/climate.py +++ b/homeassistant/components/ephember/climate.py @@ -1,9 +1,4 @@ -""" -Support for the EPH Controls Ember themostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.ephember/ -""" +"""Support for the EPH Controls Ember themostats.""" import logging from datetime import timedelta import voluptuous as vol diff --git a/homeassistant/components/epson/media_player.py b/homeassistant/components/epson/media_player.py index 38c0ffacc3231c..75be4f7fe2c1b7 100644 --- a/homeassistant/components/epson/media_player.py +++ b/homeassistant/components/epson/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Epson projector. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/media_player.epson/ -""" +"""Support for Epson projector.""" import logging import voluptuous as vol diff --git a/homeassistant/components/eq3btsmart/climate.py b/homeassistant/components/eq3btsmart/climate.py index 43a26c27ce1858..f02bd2bc9a55e2 100644 --- a/homeassistant/components/eq3btsmart/climate.py +++ b/homeassistant/components/eq3btsmart/climate.py @@ -1,9 +1,4 @@ -""" -Support for eQ-3 Bluetooth Smart thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.eq3btsmart/ -""" +"""Support for eQ-3 Bluetooth Smart thermostats.""" import logging import voluptuous as vol diff --git a/homeassistant/components/everlights/light.py b/homeassistant/components/everlights/light.py index 31e72c78fd6066..a628f25ea288f0 100644 --- a/homeassistant/components/everlights/light.py +++ b/homeassistant/components/everlights/light.py @@ -1,9 +1,4 @@ -""" -Support for EverLights lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.everlights/ -""" +"""Support for EverLights lights.""" import logging from datetime import timedelta from typing import Tuple diff --git a/homeassistant/components/facebook/notify.py b/homeassistant/components/facebook/notify.py index 2c691661a29d77..625b922927c552 100644 --- a/homeassistant/components/facebook/notify.py +++ b/homeassistant/components/facebook/notify.py @@ -1,9 +1,4 @@ -""" -Facebook platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.facebook/ -""" +"""Facebook platform for notify component.""" import json import logging diff --git a/homeassistant/components/facebox/image_processing.py b/homeassistant/components/facebox/image_processing.py index 2fbd1c5c81c4a0..2b4f184c3fd49c 100644 --- a/homeassistant/components/facebox/image_processing.py +++ b/homeassistant/components/facebox/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will perform facial detection and identification via facebox. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.facebox -""" +"""Component for facial detection and identification via facebox.""" import base64 import logging diff --git a/homeassistant/components/familyhub/camera.py b/homeassistant/components/familyhub/camera.py index e14bd9f10987ea..18aa969132da79 100644 --- a/homeassistant/components/familyhub/camera.py +++ b/homeassistant/components/familyhub/camera.py @@ -1,9 +1,4 @@ -""" -Family Hub camera for Samsung Refrigerators. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/camera.familyhub/ -""" +"""Family Hub camera for Samsung Refrigerators.""" import logging import voluptuous as vol diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index 50d6802c4d241f..e67ba390a98e53 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -1,9 +1,4 @@ -""" -Provides functionality to interact with fans. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/fan/ -""" +"""Provides functionality to interact with fans.""" from datetime import timedelta import functools as ft import logging diff --git a/homeassistant/components/fedex/sensor.py b/homeassistant/components/fedex/sensor.py index 54c319e6441e20..f535195bd075ac 100644 --- a/homeassistant/components/fedex/sensor.py +++ b/homeassistant/components/fedex/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for Fedex packages. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fedex/ -""" +"""Sensor for Fedex packages.""" from collections import defaultdict import logging from datetime import timedelta diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index 07e56cfd51fdf7..8bca13cfbb7c94 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -1,9 +1,4 @@ -""" -Support for Cameras with FFmpeg as decoder. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.ffmpeg/ -""" +"""Support for Cameras with FFmpeg as decoder.""" import asyncio import logging diff --git a/homeassistant/components/ffmpeg_motion/binary_sensor.py b/homeassistant/components/ffmpeg_motion/binary_sensor.py index 8183b0e66a61a9..c274d84329e62d 100644 --- a/homeassistant/components/ffmpeg_motion/binary_sensor.py +++ b/homeassistant/components/ffmpeg_motion/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Provides a binary sensor which is a collection of ffmpeg tools. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.ffmpeg_motion/ -""" +"""Provides a binary sensor which is a collection of ffmpeg tools.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ffmpeg_noise/binary_sensor.py b/homeassistant/components/ffmpeg_noise/binary_sensor.py index 56edf1ddfd6a2e..7efcc3deda2637 100644 --- a/homeassistant/components/ffmpeg_noise/binary_sensor.py +++ b/homeassistant/components/ffmpeg_noise/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Provides a binary sensor which is a collection of ffmpeg tools. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.ffmpeg_noise/ -""" +"""Provides a binary sensor which is a collection of ffmpeg tools.""" import logging import voluptuous as vol diff --git a/homeassistant/components/file/notify.py b/homeassistant/components/file/notify.py index d449476469baea..07718dcf36c773 100644 --- a/homeassistant/components/file/notify.py +++ b/homeassistant/components/file/notify.py @@ -1,9 +1,4 @@ -""" -Support for file notification. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.file/ -""" +"""Support for file notification.""" import logging import os diff --git a/homeassistant/components/file/sensor.py b/homeassistant/components/file/sensor.py index 3e2a5c21be8138..a618c1e56dc83b 100644 --- a/homeassistant/components/file/sensor.py +++ b/homeassistant/components/file/sensor.py @@ -1,9 +1,4 @@ -""" -Support for sensor value(s) stored in local files. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.file/ -""" +"""Support for sensor value(s) stored in local files.""" import os import logging diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 4df858fda23dba..3e1394c72d68e7 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for monitoring the size of a file. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.filesize/ -""" +"""Sensor for monitoring the size of a file.""" import datetime import logging import os diff --git a/homeassistant/components/filter/sensor.py b/homeassistant/components/filter/sensor.py index 92e2cc751ac758..734caa3127028e 100644 --- a/homeassistant/components/filter/sensor.py +++ b/homeassistant/components/filter/sensor.py @@ -1,9 +1,4 @@ -""" -Allows the creation of a sensor that filters state property. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.filter/ -""" +"""Allows the creation of a sensor that filters state property.""" import logging import statistics from collections import deque, Counter diff --git a/homeassistant/components/fints/sensor.py b/homeassistant/components/fints/sensor.py index e5dae70070bedb..dce52785fbf269 100644 --- a/homeassistant/components/fints/sensor.py +++ b/homeassistant/components/fints/sensor.py @@ -1,9 +1,4 @@ -""" -Read the balance of your bank accounts via FinTS. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fints/ -""" +"""Read the balance of your bank accounts via FinTS.""" from collections import namedtuple from datetime import timedelta diff --git a/homeassistant/components/fitbit/sensor.py b/homeassistant/components/fitbit/sensor.py index d5d9150e4e8515..abbe69c3e1d7db 100644 --- a/homeassistant/components/fitbit/sensor.py +++ b/homeassistant/components/fitbit/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Fitbit API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fitbit/ -""" +"""Support for the Fitbit API.""" import os import logging import datetime diff --git a/homeassistant/components/fixer/sensor.py b/homeassistant/components/fixer/sensor.py index c46fa751319571..f746d2008e10f3 100644 --- a/homeassistant/components/fixer/sensor.py +++ b/homeassistant/components/fixer/sensor.py @@ -1,9 +1,4 @@ -""" -Currency exchange rate support that comes from fixer.io. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fixer/ -""" +"""Currency exchange rate support that comes from fixer.io.""" from datetime import timedelta import logging diff --git a/homeassistant/components/flic/binary_sensor.py b/homeassistant/components/flic/binary_sensor.py index baf1d469b28b8b..083ac01ab4a2d9 100644 --- a/homeassistant/components/flic/binary_sensor.py +++ b/homeassistant/components/flic/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support to use flic buttons as a binary sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.flic/ -""" +"""Support to use flic buttons as a binary sensor.""" import logging import threading diff --git a/homeassistant/components/flock/notify.py b/homeassistant/components/flock/notify.py index b37483d2f1318a..384bf26599ad22 100644 --- a/homeassistant/components/flock/notify.py +++ b/homeassistant/components/flock/notify.py @@ -1,9 +1,4 @@ -""" -Flock platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.flock/ -""" +"""Flock platform for notify component.""" import asyncio import logging diff --git a/homeassistant/components/flunearyou/sensor.py b/homeassistant/components/flunearyou/sensor.py index 8dfb330cf5cebe..65de2c6ae43e0d 100644 --- a/homeassistant/components/flunearyou/sensor.py +++ b/homeassistant/components/flunearyou/sensor.py @@ -1,9 +1,4 @@ -""" -Support for user- and CDC-based flu info sensors from Flu Near You. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.flunearyou/ -""" +"""Support for user- and CDC-based flu info sensors from Flu Near You.""" import logging from datetime import timedelta diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 17c288da6c20d9..0ed14c49ec851c 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -1,9 +1,4 @@ -""" -Support for Flux lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.flux_led/ -""" +"""Support for Flux lights.""" import logging import socket import random diff --git a/homeassistant/components/folder/sensor.py b/homeassistant/components/folder/sensor.py index 8101bbd059aaf9..d742166a192aa4 100644 --- a/homeassistant/components/folder/sensor.py +++ b/homeassistant/components/folder/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for monitoring the contents of a folder. - -For more details about this platform, refer to the documentation at -https://home-assistant.io/components/sensor.folder/ -""" +"""Sensor for monitoring the contents of a folder.""" from datetime import timedelta import glob import logging diff --git a/homeassistant/components/foobot/sensor.py b/homeassistant/components/foobot/sensor.py index 62139c53c4b8fb..2eeca5243a66f5 100644 --- a/homeassistant/components/foobot/sensor.py +++ b/homeassistant/components/foobot/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Foobot indoor air quality monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.foobot/ -""" +"""Support for the Foobot indoor air quality monitor.""" import asyncio import logging from datetime import timedelta diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index b6f2162d57a9d9..9852f617d01006 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -1,9 +1,4 @@ -""" -This component provides basic support for Foscam IP cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.foscam/ -""" +"""This component provides basic support for Foscam IP cameras.""" import logging import voluptuous as vol diff --git a/homeassistant/components/free_mobile/notify.py b/homeassistant/components/free_mobile/notify.py index 1c6804f6d82b98..03beef52357af6 100644 --- a/homeassistant/components/free_mobile/notify.py +++ b/homeassistant/components/free_mobile/notify.py @@ -1,9 +1,4 @@ -""" -Support for thr Free Mobile SMS platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.free_mobile/ -""" +"""Support for thr Free Mobile SMS platform.""" import logging import voluptuous as vol diff --git a/homeassistant/components/freedns/__init__.py b/homeassistant/components/freedns/__init__.py index edb3a57c28ca55..a0b1475774565b 100644 --- a/homeassistant/components/freedns/__init__.py +++ b/homeassistant/components/freedns/__init__.py @@ -1,9 +1,4 @@ -""" -Integrate with FreeDNS Dynamic DNS service at freedns.afraid.org. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/freedns/ -""" +"""Integrate with FreeDNS Dynamic DNS service at freedns.afraid.org.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/fritz/device_tracker.py b/homeassistant/components/fritz/device_tracker.py index 75e280fe908a0c..3e3e04f44475fb 100644 --- a/homeassistant/components/fritz/device_tracker.py +++ b/homeassistant/components/fritz/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for FRITZ!Box routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.fritz/ -""" +"""Support for FRITZ!Box routers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/fritzbox_callmonitor/sensor.py b/homeassistant/components/fritzbox_callmonitor/sensor.py index 397f08d8a7c048..a6641bc14ad5b5 100644 --- a/homeassistant/components/fritzbox_callmonitor/sensor.py +++ b/homeassistant/components/fritzbox_callmonitor/sensor.py @@ -1,9 +1,4 @@ -""" -A sensor to monitor incoming and outgoing phone calls on a Fritz!Box router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fritzbox_callmonitor/ -""" +"""Sensor to monitor incoming/outgoing phone calls on a Fritz!Box router.""" import logging import socket import threading diff --git a/homeassistant/components/fritzbox_netmonitor/sensor.py b/homeassistant/components/fritzbox_netmonitor/sensor.py index 356c1424012a0f..93f834a894d3b7 100644 --- a/homeassistant/components/fritzbox_netmonitor/sensor.py +++ b/homeassistant/components/fritzbox_netmonitor/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring an AVM Fritz!Box router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.fritzbox_netmonitor/ -""" +"""Support for monitoring an AVM Fritz!Box router.""" import logging from datetime import timedelta from requests.exceptions import RequestException diff --git a/homeassistant/components/fritzdect/switch.py b/homeassistant/components/fritzdect/switch.py index 0d9008552a11ab..449ae5a76f13ae 100644 --- a/homeassistant/components/fritzdect/switch.py +++ b/homeassistant/components/fritzdect/switch.py @@ -1,9 +1,4 @@ -""" -Support for FRITZ!DECT Switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.fritzdect/ -""" +"""Support for FRITZ!DECT Switches.""" import logging from requests.exceptions import RequestException, HTTPError diff --git a/homeassistant/components/frontier_silicon/media_player.py b/homeassistant/components/frontier_silicon/media_player.py index ed7041a3b82931..4f28d83e6cfe36 100644 --- a/homeassistant/components/frontier_silicon/media_player.py +++ b/homeassistant/components/frontier_silicon/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Frontier Silicon Devices (Medion, Hama, Auna,...). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.frontier_silicon/ -""" +"""Support for Frontier Silicon Devices (Medion, Hama, Auna,...).""" import logging import voluptuous as vol diff --git a/homeassistant/components/futurenow/light.py b/homeassistant/components/futurenow/light.py index 8b0a809b667bb5..4b570fd0a4dcdb 100644 --- a/homeassistant/components/futurenow/light.py +++ b/homeassistant/components/futurenow/light.py @@ -1,9 +1,4 @@ -""" -Support for FutureNow Ethernet unit outputs as Lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.futurenow/ -""" +"""Support for FutureNow Ethernet unit outputs as Lights.""" import logging diff --git a/homeassistant/components/garadget/cover.py b/homeassistant/components/garadget/cover.py index 426afc6d3147e4..b3c7c7c1215758 100644 --- a/homeassistant/components/garadget/cover.py +++ b/homeassistant/components/garadget/cover.py @@ -1,9 +1,4 @@ -""" -Platform for the Garadget cover component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/garadget/ -""" +"""Platform for the Garadget cover component.""" import logging import requests diff --git a/homeassistant/components/gc100/binary_sensor.py b/homeassistant/components/gc100/binary_sensor.py index ec69d1eec83421..9588506af77666 100644 --- a/homeassistant/components/gc100/binary_sensor.py +++ b/homeassistant/components/gc100/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for binary sensor using GC100. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.gc100/ -""" +"""Support for binary sensor using GC100.""" import voluptuous as vol from homeassistant.components.binary_sensor import ( diff --git a/homeassistant/components/gc100/switch.py b/homeassistant/components/gc100/switch.py index 94c824fa5d784d..1ffb2726495ffc 100644 --- a/homeassistant/components/gc100/switch.py +++ b/homeassistant/components/gc100/switch.py @@ -1,9 +1,4 @@ -""" -Support for switches using GC100. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.gc100/ -""" +"""Support for switches using GC100.""" import voluptuous as vol from homeassistant.components.switch import PLATFORM_SCHEMA diff --git a/homeassistant/components/gearbest/sensor.py b/homeassistant/components/gearbest/sensor.py index 5521e9a644c1ec..e4f85a1892dfc7 100644 --- a/homeassistant/components/gearbest/sensor.py +++ b/homeassistant/components/gearbest/sensor.py @@ -1,9 +1,4 @@ -""" -Parse prices of an item from gearbest. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gearbest/ -""" +"""Parse prices of an item from gearbest.""" import logging from datetime import timedelta diff --git a/homeassistant/components/geizhals/sensor.py b/homeassistant/components/geizhals/sensor.py index 66cab473f49f10..d619d768c234e3 100644 --- a/homeassistant/components/geizhals/sensor.py +++ b/homeassistant/components/geizhals/sensor.py @@ -1,9 +1,4 @@ -""" -Parse prices of a device from geizhals. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.geizhals/ -""" +"""Parse prices of a device from geizhals.""" import logging from datetime import timedelta diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index c9f8616f637ecc..bfe42a5b080d52 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -1,9 +1,4 @@ -""" -Support for IP Cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.generic/ -""" +"""Support for IP Cameras.""" import asyncio import logging diff --git a/homeassistant/components/generic_thermostat/climate.py b/homeassistant/components/generic_thermostat/climate.py index 1eb0f8e79db484..35efa82c8a3c1f 100644 --- a/homeassistant/components/generic_thermostat/climate.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -1,9 +1,4 @@ -""" -Adds support for generic thermostat units. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.generic_thermostat/ -""" +"""Adds support for generic thermostat units.""" import asyncio import logging diff --git a/homeassistant/components/geofency/device_tracker.py b/homeassistant/components/geofency/device_tracker.py index 5e7b8291840fec..0a1a9d5f32edc6 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for the Geofency device tracker platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.geofency/ -""" +"""Support for the Geofency device tracker platform.""" import logging from homeassistant.components.device_tracker import ( diff --git a/homeassistant/components/github/sensor.py b/homeassistant/components/github/sensor.py index 335dbc668d9248..5a86233d561ff7 100644 --- a/homeassistant/components/github/sensor.py +++ b/homeassistant/components/github/sensor.py @@ -1,9 +1,4 @@ -""" -Support for GitHub. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.github/ -""" +"""Support for GitHub.""" from datetime import timedelta import logging import voluptuous as vol diff --git a/homeassistant/components/gitlab_ci/sensor.py b/homeassistant/components/gitlab_ci/sensor.py index 7f3b444bb75fae..dd574b348d8239 100644 --- a/homeassistant/components/gitlab_ci/sensor.py +++ b/homeassistant/components/gitlab_ci/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for retrieving latest GitLab CI job information. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gitlab_ci/ -""" +"""Sensor for retrieving latest GitLab CI job information.""" from datetime import timedelta import logging diff --git a/homeassistant/components/gitter/sensor.py b/homeassistant/components/gitter/sensor.py index 97cd3f662d519a..2af9c20fb29094 100644 --- a/homeassistant/components/gitter/sensor.py +++ b/homeassistant/components/gitter/sensor.py @@ -1,9 +1,4 @@ -""" -Support for displaying details about a Gitter.im chat room. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gitter/ -""" +"""Support for displaying details about a Gitter.im chat room.""" import logging import voluptuous as vol diff --git a/homeassistant/components/glances/sensor.py b/homeassistant/components/glances/sensor.py index 53db254e4b3934..e59b9144b4c28f 100644 --- a/homeassistant/components/glances/sensor.py +++ b/homeassistant/components/glances/sensor.py @@ -1,9 +1,4 @@ -""" -Support gathering system information of hosts which are running glances. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.glances/ -""" +"""Support gathering system information of hosts which are running glances.""" from datetime import timedelta import logging diff --git a/homeassistant/components/gntp/notify.py b/homeassistant/components/gntp/notify.py index 915d33668b4e05..fb3e96e83ab827 100644 --- a/homeassistant/components/gntp/notify.py +++ b/homeassistant/components/gntp/notify.py @@ -1,9 +1,4 @@ -""" -GNTP (aka Growl) notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.gntp/ -""" +"""GNTP (aka Growl) notification service.""" import logging import os diff --git a/homeassistant/components/gogogate2/cover.py b/homeassistant/components/gogogate2/cover.py index accc4f9ec98222..4d40ddd2c72ee9 100644 --- a/homeassistant/components/gogogate2/cover.py +++ b/homeassistant/components/gogogate2/cover.py @@ -1,9 +1,4 @@ -""" -Support for Gogogate2 garage Doors. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/cover.gogogate2/ -""" +"""Support for Gogogate2 garage Doors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/google_assistant/http.py b/homeassistant/components/google_assistant/http.py index cbe2015f4f900d..11d8a3841650d7 100644 --- a/homeassistant/components/google_assistant/http.py +++ b/homeassistant/components/google_assistant/http.py @@ -1,9 +1,4 @@ -""" -Support for Google Actions Smart Home Control. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/google_assistant/ -""" +"""Support for Google Actions Smart Home Control.""" import logging from aiohttp.web import Request, Response diff --git a/homeassistant/components/google_maps/device_tracker.py b/homeassistant/components/google_maps/device_tracker.py index 3de60d6cb38766..7bc9be00b8cc66 100644 --- a/homeassistant/components/google_maps/device_tracker.py +++ b/homeassistant/components/google_maps/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Google Maps location sharing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.google_maps/ -""" +"""Support for Google Maps location sharing.""" from datetime import timedelta import logging diff --git a/homeassistant/components/google_travel_time/sensor.py b/homeassistant/components/google_travel_time/sensor.py index 86b1a7aff44503..b448830ab02aa6 100644 --- a/homeassistant/components/google_travel_time/sensor.py +++ b/homeassistant/components/google_travel_time/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Google travel time sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.google_travel_time/ -""" +"""Support for Google travel time sensors.""" import logging from datetime import datetime from datetime import timedelta diff --git a/homeassistant/components/google_wifi/sensor.py b/homeassistant/components/google_wifi/sensor.py index b78e9afb8b99fd..202e2a0eb466be 100644 --- a/homeassistant/components/google_wifi/sensor.py +++ b/homeassistant/components/google_wifi/sensor.py @@ -1,9 +1,4 @@ -""" -Support for retrieving status info from Google Wifi/OnHub routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.google_wifi/ -""" +"""Support for retrieving status info from Google Wifi/OnHub routers.""" import logging from datetime import timedelta diff --git a/homeassistant/components/gpmdp/media_player.py b/homeassistant/components/gpmdp/media_player.py index c72d14ebb8a6ae..788126b957f9fc 100644 --- a/homeassistant/components/gpmdp/media_player.py +++ b/homeassistant/components/gpmdp/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Google Play Music Desktop Player. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.gpmdp/ -""" +"""Support for Google Play Music Desktop Player.""" import json import logging import socket diff --git a/homeassistant/components/gpsd/sensor.py b/homeassistant/components/gpsd/sensor.py index b1ce428e1f2866..62307cb1011631 100644 --- a/homeassistant/components/gpsd/sensor.py +++ b/homeassistant/components/gpsd/sensor.py @@ -1,9 +1,4 @@ -""" -Support for GPSD. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gpsd/ -""" +"""Support for GPSD.""" import logging import voluptuous as vol diff --git a/homeassistant/components/greeneye_monitor/sensor.py b/homeassistant/components/greeneye_monitor/sensor.py index 4dee9d69b42e94..8321bb768cabf0 100644 --- a/homeassistant/components/greeneye_monitor/sensor.py +++ b/homeassistant/components/greeneye_monitor/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the sensors in a GreenEye Monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensors.greeneye_monitor_temperature/ -""" +"""Support for the sensors in a GreenEye Monitor.""" import logging from homeassistant.const import CONF_NAME, CONF_TEMPERATURE_UNIT, POWER_WATT diff --git a/homeassistant/components/greenwave/light.py b/homeassistant/components/greenwave/light.py index 0c484a0e3f49d9..b8efe8ae17dcba 100644 --- a/homeassistant/components/greenwave/light.py +++ b/homeassistant/components/greenwave/light.py @@ -1,9 +1,4 @@ -""" -Support for Greenwave Reality (TCP Connected) lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.greenwave/ -""" +"""Support for Greenwave Reality (TCP Connected) lights.""" import logging from datetime import timedelta diff --git a/homeassistant/components/group/cover.py b/homeassistant/components/group/cover.py index c1825211433716..385d20949d6d69 100644 --- a/homeassistant/components/group/cover.py +++ b/homeassistant/components/group/cover.py @@ -1,9 +1,4 @@ -""" -This platform allows several cover to be grouped into one cover. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover.group/ -""" +"""This platform allows several cover to be grouped into one cover.""" import logging import voluptuous as vol diff --git a/homeassistant/components/group/light.py b/homeassistant/components/group/light.py index c37c5cc4e8e6ee..170e93398a1c26 100644 --- a/homeassistant/components/group/light.py +++ b/homeassistant/components/group/light.py @@ -1,9 +1,4 @@ -""" -This platform allows several lights to be grouped into one light. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.group/ -""" +"""This platform allows several lights to be grouped into one light.""" from collections import Counter import itertools import logging diff --git a/homeassistant/components/group/notify.py b/homeassistant/components/group/notify.py index 200464bb8cb318..b59c49563e21b7 100644 --- a/homeassistant/components/group/notify.py +++ b/homeassistant/components/group/notify.py @@ -1,9 +1,4 @@ -""" -Group platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.group/ -""" +"""Group platform for notify component.""" import asyncio from collections.abc import Mapping from copy import deepcopy diff --git a/homeassistant/components/gstreamer/media_player.py b/homeassistant/components/gstreamer/media_player.py index c6571894472cbe..094a561d310ea5 100644 --- a/homeassistant/components/gstreamer/media_player.py +++ b/homeassistant/components/gstreamer/media_player.py @@ -1,9 +1,4 @@ -""" -Play media via gstreamer. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.gstreamer/ -""" +"""Play media via gstreamer.""" import logging import voluptuous as vol diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index c94f97d93dc0f2..9e89a8ad844de3 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -1,9 +1,4 @@ -""" -Support for GTFS (Google/General Transport Format Schema). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gtfs/ -""" +"""Support for GTFS (Google/General Transport Format Schema).""" import datetime import logging import os diff --git a/homeassistant/components/gtt/sensor.py b/homeassistant/components/gtt/sensor.py index a64c743381df9e..659984fadea8f9 100644 --- a/homeassistant/components/gtt/sensor.py +++ b/homeassistant/components/gtt/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor to get GTT's timetable for a stop. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.gtt/ -""" +"""Sensor to get GTT's timetable for a stop.""" import logging from datetime import timedelta, datetime diff --git a/homeassistant/components/harman_kardon_avr/media_player.py b/homeassistant/components/harman_kardon_avr/media_player.py index 334757c086dbaf..cec0ac4f5c8653 100644 --- a/homeassistant/components/harman_kardon_avr/media_player.py +++ b/homeassistant/components/harman_kardon_avr/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with an Harman/Kardon or JBL AVR. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.harman_kardon_avr/ -""" +"""Support for interface with an Harman/Kardon or JBL AVR.""" import logging import voluptuous as vol diff --git a/homeassistant/components/haveibeenpwned/sensor.py b/homeassistant/components/haveibeenpwned/sensor.py index a4ae2349e24258..72cc5ced3effb4 100644 --- a/homeassistant/components/haveibeenpwned/sensor.py +++ b/homeassistant/components/haveibeenpwned/sensor.py @@ -1,9 +1,4 @@ -""" -Support for haveibeenpwned (email breaches) sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.haveibeenpwned/ -""" +"""Support for haveibeenpwned (email breaches) sensor.""" from datetime import timedelta import logging diff --git a/homeassistant/components/hddtemp/sensor.py b/homeassistant/components/hddtemp/sensor.py index 52514a2de39729..6d96f244f58641 100644 --- a/homeassistant/components/hddtemp/sensor.py +++ b/homeassistant/components/hddtemp/sensor.py @@ -1,9 +1,4 @@ -""" -Support for getting the disk temperature of a host. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.hddtemp/ -""" +"""Support for getting the disk temperature of a host.""" import logging from datetime import timedelta from telnetlib import Telnet diff --git a/homeassistant/components/heatmiser/climate.py b/homeassistant/components/heatmiser/climate.py index ff495706be77c0..fc9057bc905699 100644 --- a/homeassistant/components/heatmiser/climate.py +++ b/homeassistant/components/heatmiser/climate.py @@ -1,9 +1,4 @@ -""" -Support for the PRT Heatmiser themostats using the V3 protocol. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.heatmiser/ -""" +"""Support for the PRT Heatmiser themostats using the V3 protocol.""" import logging import voluptuous as vol diff --git a/homeassistant/components/hikvision/binary_sensor.py b/homeassistant/components/hikvision/binary_sensor.py index fdefc40d8fdf01..a6a82c9ee1b814 100644 --- a/homeassistant/components/hikvision/binary_sensor.py +++ b/homeassistant/components/hikvision/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Hikvision event stream events represented as binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.hikvision/ -""" +"""Support for Hikvision event stream events represented as binary sensors.""" import logging from datetime import timedelta import voluptuous as vol diff --git a/homeassistant/components/hikvisioncam/switch.py b/homeassistant/components/hikvisioncam/switch.py index 3a3dec26e1d030..6e5dcdac9aae6b 100644 --- a/homeassistant/components/hikvisioncam/switch.py +++ b/homeassistant/components/hikvisioncam/switch.py @@ -1,9 +1,4 @@ -""" -Support turning on/off motion detection on Hikvision cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.hikvision/ -""" +"""Support turning on/off motion detection on Hikvision cameras.""" import logging import voluptuous as vol diff --git a/homeassistant/components/hipchat/notify.py b/homeassistant/components/hipchat/notify.py index 9e415171f29c4e..5128b8beea341c 100644 --- a/homeassistant/components/hipchat/notify.py +++ b/homeassistant/components/hipchat/notify.py @@ -1,9 +1,4 @@ -""" -HipChat platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.hipchat/ -""" +"""HipChat platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index f5b76c89abab22..f1eea4dd693086 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -1,9 +1,4 @@ -""" -Component to make instant statistics about your history. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.history_stats/ -""" +"""Component to make instant statistics about your history.""" import datetime import logging import math diff --git a/homeassistant/components/hitron_coda/device_tracker.py b/homeassistant/components/hitron_coda/device_tracker.py index 72817ca695c85f..e6f68d704fd02f 100644 --- a/homeassistant/components/hitron_coda/device_tracker.py +++ b/homeassistant/components/hitron_coda/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for the Hitron CODA-4582U, provided by Rogers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.hitron_coda/ -""" +"""Support for the Hitron CODA-4582U, provided by Rogers.""" import logging from collections import namedtuple diff --git a/homeassistant/components/homematic/notify.py b/homeassistant/components/homematic/notify.py index 021560eee3cbb1..9054c1fa0ad6ca 100644 --- a/homeassistant/components/homematic/notify.py +++ b/homeassistant/components/homematic/notify.py @@ -1,9 +1,4 @@ -""" -Notification support for Homematic. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.homematic/ -""" +"""Notification support for Homematic.""" import logging import voluptuous as vol diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 55a7fb5aa4859e..7460ed6e9d0a8c 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -1,9 +1,4 @@ -""" -Support for Honeywell Round Connected and Honeywell Evohome thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.honeywell/ -""" +"""Support for Honeywell Round Connected and Honeywell Evohome thermostats.""" import logging import datetime diff --git a/homeassistant/components/hook/switch.py b/homeassistant/components/hook/switch.py index fdc13f59d2863e..7a11c1dd8b75e6 100644 --- a/homeassistant/components/hook/switch.py +++ b/homeassistant/components/hook/switch.py @@ -1,9 +1,4 @@ -""" -Support Hook, available at hooksmarthome.com. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.hook/ -""" +"""Support Hook, available at hooksmarthome.com.""" import logging import asyncio diff --git a/homeassistant/components/horizon/media_player.py b/homeassistant/components/horizon/media_player.py index 3b1ae36152d471..51168e4ef2e1a7 100644 --- a/homeassistant/components/horizon/media_player.py +++ b/homeassistant/components/horizon/media_player.py @@ -1,9 +1,4 @@ -""" -Support for the Unitymedia Horizon HD Recorder. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/media_player.horizon/ -""" +"""Support for the Unitymedia Horizon HD Recorder.""" from datetime import timedelta import logging diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py index 8744d0afb283b2..f7b220ff1387a1 100644 --- a/homeassistant/components/html5/notify.py +++ b/homeassistant/components/html5/notify.py @@ -1,9 +1,4 @@ -""" -HTML5 Push Messaging notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.html5/ -""" +"""HTML5 Push Messaging notification service.""" import datetime from functools import partial import json diff --git a/homeassistant/components/htu21d/sensor.py b/homeassistant/components/htu21d/sensor.py index 4f8665b2011b6c..17182bb833d8c6 100644 --- a/homeassistant/components/htu21d/sensor.py +++ b/homeassistant/components/htu21d/sensor.py @@ -1,9 +1,4 @@ -""" -Support for HTU21D temperature and humidity sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.htu21d/ -""" +"""Support for HTU21D temperature and humidity sensor.""" from datetime import timedelta from functools import partial import logging diff --git a/homeassistant/components/huawei_router/device_tracker.py b/homeassistant/components/huawei_router/device_tracker.py index 18f3c0b8c62ee8..88e2a57a579b01 100644 --- a/homeassistant/components/huawei_router/device_tracker.py +++ b/homeassistant/components/huawei_router/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for HUAWEI routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.huawei_router/ -""" +"""Support for HUAWEI routers.""" import base64 import logging import re diff --git a/homeassistant/components/ialarm/alarm_control_panel.py b/homeassistant/components/ialarm/alarm_control_panel.py index df975ef00ac44f..8152c2496e603c 100644 --- a/homeassistant/components/ialarm/alarm_control_panel.py +++ b/homeassistant/components/ialarm/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Interfaces with iAlarm control panels. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.ialarm/ -""" +"""Interfaces with iAlarm control panels.""" import logging import re diff --git a/homeassistant/components/icloud/device_tracker.py b/homeassistant/components/icloud/device_tracker.py index 9ac77cf7f016f3..1d0e0d2fafb0f6 100644 --- a/homeassistant/components/icloud/device_tracker.py +++ b/homeassistant/components/icloud/device_tracker.py @@ -1,9 +1,4 @@ -""" -Platform that supports scanning iCloud. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.icloud/ -""" +"""Platform that supports scanning iCloud.""" import logging import random import os diff --git a/homeassistant/components/iglo/light.py b/homeassistant/components/iglo/light.py index 9dca5f8e5f5180..6851141efb46be 100644 --- a/homeassistant/components/iglo/light.py +++ b/homeassistant/components/iglo/light.py @@ -1,9 +1,4 @@ -""" -Support for lights under the iGlo brand. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.iglo/ -""" +"""Support for lights under the iGlo brand.""" import logging import math diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index aa3b2db736953c..e5193985629629 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -1,9 +1,4 @@ -""" -Provides functionality to interact with image processing services. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/image_processing/ -""" +"""Provides functionality to interact with image processing services.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/imap/sensor.py b/homeassistant/components/imap/sensor.py index 571d05e78e953b..5ff23eb8e5d5c4 100644 --- a/homeassistant/components/imap/sensor.py +++ b/homeassistant/components/imap/sensor.py @@ -1,9 +1,4 @@ -""" -IMAP sensor support. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.imap/ -""" +"""IMAP sensor support.""" import asyncio import logging diff --git a/homeassistant/components/imap_email_content/sensor.py b/homeassistant/components/imap_email_content/sensor.py index 714a6c387812ca..950007b462b21c 100644 --- a/homeassistant/components/imap_email_content/sensor.py +++ b/homeassistant/components/imap_email_content/sensor.py @@ -1,9 +1,4 @@ -""" -Email sensor support. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.imap_email_content/ -""" +"""Email sensor support.""" import logging import datetime import email diff --git a/homeassistant/components/influxdb/sensor.py b/homeassistant/components/influxdb/sensor.py index 9dbb4787df73d2..3bec7e3c657780 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -1,9 +1,4 @@ -""" -InfluxDB component which allows you to get data from an Influx database. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.influxdb/ -""" +"""InfluxDB component which allows you to get data from an Influx database.""" from datetime import timedelta import logging diff --git a/homeassistant/components/integration/sensor.py b/homeassistant/components/integration/sensor.py index 9250c8dde055ee..3a72c81fa11ac0 100644 --- a/homeassistant/components/integration/sensor.py +++ b/homeassistant/components/integration/sensor.py @@ -1,9 +1,4 @@ -""" -Numeric integration of data coming from a source sensor over time. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.integration/ -""" +"""Numeric integration of data coming from a source sensor over time.""" import logging from decimal import Decimal, DecimalException diff --git a/homeassistant/components/islamic_prayer_times/sensor.py b/homeassistant/components/islamic_prayer_times/sensor.py index 50331435491726..9efbc237e30a5e 100644 --- a/homeassistant/components/islamic_prayer_times/sensor.py +++ b/homeassistant/components/islamic_prayer_times/sensor.py @@ -1,9 +1,4 @@ -""" -Platform to retrieve Islamic prayer times information for Home Assistant. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.islamic_prayer_times/ -""" +"""Platform to retrieve Islamic prayer times information for Home Assistant.""" import logging from datetime import datetime, timedelta diff --git a/homeassistant/components/iss/binary_sensor.py b/homeassistant/components/iss/binary_sensor.py index b986c51ddaa14d..381bc16791826e 100644 --- a/homeassistant/components/iss/binary_sensor.py +++ b/homeassistant/components/iss/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for International Space Station data sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.iss/ -""" +"""Support for International Space Station data sensor.""" import logging from datetime import timedelta diff --git a/homeassistant/components/itunes/media_player.py b/homeassistant/components/itunes/media_player.py index f8380032aea4cd..8451d751954cc4 100644 --- a/homeassistant/components/itunes/media_player.py +++ b/homeassistant/components/itunes/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing to iTunes API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.itunes/ -""" +"""Support for interfacing to iTunes API.""" import logging import requests diff --git a/homeassistant/components/jewish_calendar/sensor.py b/homeassistant/components/jewish_calendar/sensor.py index 65aeaf7fba9ba5..478bbed98fa6c7 100644 --- a/homeassistant/components/jewish_calendar/sensor.py +++ b/homeassistant/components/jewish_calendar/sensor.py @@ -1,9 +1,4 @@ -""" -Platform to retrieve Jewish calendar information for Home Assistant. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.jewish_calendar/ -""" +"""Platform to retrieve Jewish calendar information for Home Assistant.""" import logging import voluptuous as vol diff --git a/homeassistant/components/kankun/switch.py b/homeassistant/components/kankun/switch.py index 86e7fcdab3eff3..a8282a366ad2fc 100644 --- a/homeassistant/components/kankun/switch.py +++ b/homeassistant/components/kankun/switch.py @@ -1,9 +1,4 @@ -""" -Support for customised Kankun SP3 Wifi switch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.kankun/ -""" +"""Support for customised Kankun SP3 Wifi switch.""" import logging import requests diff --git a/homeassistant/components/keenetic_ndms2/device_tracker.py b/homeassistant/components/keenetic_ndms2/device_tracker.py index b8c2124ff0f664..f873507112dc62 100644 --- a/homeassistant/components/keenetic_ndms2/device_tracker.py +++ b/homeassistant/components/keenetic_ndms2/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Zyxel Keenetic NDMS2 based routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.keenetic_ndms2/ -""" +"""Support for Zyxel Keenetic NDMS2 based routers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/kiwi/lock.py b/homeassistant/components/kiwi/lock.py index 5d217796767d1c..0b5806425d95a7 100644 --- a/homeassistant/components/kiwi/lock.py +++ b/homeassistant/components/kiwi/lock.py @@ -1,9 +1,4 @@ -""" -Support for the KIWI.KI lock platform. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/lock.kiwi/ -""" +"""Support for the KIWI.KI lock platform.""" import logging import voluptuous as vol diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index f8d0cdc5a12cf2..81c93dba2ac417 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with the XBMC/Kodi JSON-RPC API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.kodi/ -""" +"""Support for interfacing with the XBMC/Kodi JSON-RPC API.""" import asyncio from collections import OrderedDict from functools import wraps diff --git a/homeassistant/components/kodi/notify.py b/homeassistant/components/kodi/notify.py index 7c2a60f349863c..f6ee2c47b96b7c 100644 --- a/homeassistant/components/kodi/notify.py +++ b/homeassistant/components/kodi/notify.py @@ -1,9 +1,4 @@ -""" -Kodi notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.kodi/ -""" +"""Kodi notification service.""" import logging import aiohttp diff --git a/homeassistant/components/kwb/sensor.py b/homeassistant/components/kwb/sensor.py index f490fbd5b143a0..bad0ea3cdede51 100644 --- a/homeassistant/components/kwb/sensor.py +++ b/homeassistant/components/kwb/sensor.py @@ -1,9 +1,4 @@ -""" -Support for KWB Easyfire. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.kwb/ -""" +"""Support for KWB Easyfire.""" import logging import voluptuous as vol diff --git a/homeassistant/components/lacrosse/sensor.py b/homeassistant/components/lacrosse/sensor.py index 32b1dac9250d06..9240343a5e3e69 100644 --- a/homeassistant/components/lacrosse/sensor.py +++ b/homeassistant/components/lacrosse/sensor.py @@ -1,9 +1,4 @@ -""" -Support for LaCrosse sensor components. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.lacrosse/ -""" +"""Support for LaCrosse sensor components.""" from datetime import timedelta import logging diff --git a/homeassistant/components/lannouncer/notify.py b/homeassistant/components/lannouncer/notify.py index 3b2e72b398c9ec..535940a80f50d4 100644 --- a/homeassistant/components/lannouncer/notify.py +++ b/homeassistant/components/lannouncer/notify.py @@ -1,9 +1,4 @@ -""" -Lannouncer platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.lannouncer/ -""" +"""Lannouncer platform for notify component.""" import logging import socket from urllib.parse import urlencode diff --git a/homeassistant/components/launch_library/sensor.py b/homeassistant/components/launch_library/sensor.py index aaed2f9663f441..4b42ddba268b0b 100644 --- a/homeassistant/components/launch_library/sensor.py +++ b/homeassistant/components/launch_library/sensor.py @@ -1,9 +1,4 @@ -""" -A sensor platform that give you information about the next space launch. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.launch_library/ -""" +"""A sensor platform that give you information about the next space launch.""" from datetime import timedelta import logging diff --git a/homeassistant/components/lg_netcast/media_player.py b/homeassistant/components/lg_netcast/media_player.py index 7c5d9789372406..12fee5fae96eda 100644 --- a/homeassistant/components/lg_netcast/media_player.py +++ b/homeassistant/components/lg_netcast/media_player.py @@ -1,9 +1,4 @@ -""" -Support for LG TV running on NetCast 3 or 4. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.lg_netcast/ -""" +"""Support for LG TV running on NetCast 3 or 4.""" from datetime import timedelta import logging diff --git a/homeassistant/components/lg_soundbar/media_player.py b/homeassistant/components/lg_soundbar/media_player.py index b45baf88bca2a5..2e2481a462b5a9 100644 --- a/homeassistant/components/lg_soundbar/media_player.py +++ b/homeassistant/components/lg_soundbar/media_player.py @@ -1,9 +1,4 @@ -""" -Support for LG soundbars. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.lg_soundbar/ -""" +"""Support for LG soundbars.""" import logging from homeassistant.components.media_player import ( diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index e3971cd8e420c0..db2e9ce0197b50 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -1,9 +1,4 @@ -""" -Provides functionality to interact with lights. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/light/ -""" +"""Provides functionality to interact with lights.""" import asyncio import csv from datetime import timedelta diff --git a/homeassistant/components/limitlessled/light.py b/homeassistant/components/limitlessled/light.py index 3a0225d8d650d0..4f187afa1d75df 100644 --- a/homeassistant/components/limitlessled/light.py +++ b/homeassistant/components/limitlessled/light.py @@ -1,9 +1,4 @@ -""" -Support for LimitlessLED bulbs. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.limitlessled/ -""" +"""Support for LimitlessLED bulbs.""" import logging import voluptuous as vol diff --git a/homeassistant/components/linksys_ap/device_tracker.py b/homeassistant/components/linksys_ap/device_tracker.py index 5638db4caafbde..46cc78d4e4ae36 100644 --- a/homeassistant/components/linksys_ap/device_tracker.py +++ b/homeassistant/components/linksys_ap/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Linksys Access Points. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.linksys_ap/ -""" +"""Support for Linksys Access Points.""" import base64 import logging diff --git a/homeassistant/components/linky/sensor.py b/homeassistant/components/linky/sensor.py index 7a10513016f125..35f85f15ed6332 100644 --- a/homeassistant/components/linky/sensor.py +++ b/homeassistant/components/linky/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Linky. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.linky/ -""" +"""Support for Linky.""" import logging import json from datetime import timedelta diff --git a/homeassistant/components/linux_battery/sensor.py b/homeassistant/components/linux_battery/sensor.py index 69fc145a4a5ef6..7164315de8ea42 100644 --- a/homeassistant/components/linux_battery/sensor.py +++ b/homeassistant/components/linux_battery/sensor.py @@ -1,9 +1,4 @@ -""" -Details about the built-in battery. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.linux_battery/ -""" +"""Details about the built-in battery.""" import logging import os diff --git a/homeassistant/components/litejet/light.py b/homeassistant/components/litejet/light.py index 8662afc668ebfe..e52e50ed21a858 100644 --- a/homeassistant/components/litejet/light.py +++ b/homeassistant/components/litejet/light.py @@ -1,9 +1,4 @@ -""" -Support for LiteJet lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.litejet/ -""" +"""Support for LiteJet lights.""" import logging from homeassistant.components import litejet diff --git a/homeassistant/components/litejet/switch.py b/homeassistant/components/litejet/switch.py index b9755569fd23db..9972dcb9f44797 100644 --- a/homeassistant/components/litejet/switch.py +++ b/homeassistant/components/litejet/switch.py @@ -1,9 +1,4 @@ -""" -Support for LiteJet switch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.litejet/ -""" +"""Support for LiteJet switch.""" import logging from homeassistant.components import litejet diff --git a/homeassistant/components/liveboxplaytv/media_player.py b/homeassistant/components/liveboxplaytv/media_player.py index f69c3c67aec9ad..1ee9931d233c0d 100644 --- a/homeassistant/components/liveboxplaytv/media_player.py +++ b/homeassistant/components/liveboxplaytv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with an Orange Livebox Play TV appliance. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.liveboxplaytv/ -""" +"""Support for interface with an Orange Livebox Play TV appliance.""" from datetime import timedelta import logging diff --git a/homeassistant/components/llamalab_automate/notify.py b/homeassistant/components/llamalab_automate/notify.py index 6b59d11e0a3fc1..d43988ada43150 100644 --- a/homeassistant/components/llamalab_automate/notify.py +++ b/homeassistant/components/llamalab_automate/notify.py @@ -1,9 +1,4 @@ -""" -LlamaLab Automate notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.llamalab_automate/ -""" +"""LlamaLab Automate notification service.""" import logging import requests diff --git a/homeassistant/components/local_file/camera.py b/homeassistant/components/local_file/camera.py index 56780d16f5683a..444f4109e98dca 100644 --- a/homeassistant/components/local_file/camera.py +++ b/homeassistant/components/local_file/camera.py @@ -1,9 +1,4 @@ -""" -Camera that loads a picture from a local file. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.local_file/ -""" +"""Camera that loads a picture from a local file.""" import logging import mimetypes import os diff --git a/homeassistant/components/locative/device_tracker.py b/homeassistant/components/locative/device_tracker.py index 9dbf7e74fe33e8..51135f4e21a941 100644 --- a/homeassistant/components/locative/device_tracker.py +++ b/homeassistant/components/locative/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for the Locative platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.locative/ -""" +"""Support for the Locative platform.""" import logging from homeassistant.components.device_tracker import ( diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index 71c838679fbf9b..fe5286ba813daf 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with various locks that can be controlled remotely. - -For more details about this component, please refer to the documentation -at https://home-assistant.io/components/lock/ -""" +"""Component to interface with locks that can be controlled remotely.""" from datetime import timedelta import functools as ft import logging diff --git a/homeassistant/components/lockitron/lock.py b/homeassistant/components/lockitron/lock.py index b190a5cd2cd826..0ec838f4d4bcb3 100644 --- a/homeassistant/components/lockitron/lock.py +++ b/homeassistant/components/lockitron/lock.py @@ -1,9 +1,4 @@ -""" -Lockitron lock platform. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/lockitron/ -""" +"""Lockitron lock platform.""" import logging import requests diff --git a/homeassistant/components/london_air/sensor.py b/homeassistant/components/london_air/sensor.py index afb50d766f4eb8..fbdc8966ad05ee 100644 --- a/homeassistant/components/london_air/sensor.py +++ b/homeassistant/components/london_air/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for checking the status of London air. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.london_air/ -""" +"""Sensor for checking the status of London air.""" from datetime import timedelta import logging diff --git a/homeassistant/components/london_underground/sensor.py b/homeassistant/components/london_underground/sensor.py index 1c93d6a1bcb1b6..948b60b1b79d61 100644 --- a/homeassistant/components/london_underground/sensor.py +++ b/homeassistant/components/london_underground/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for checking the status of London Underground tube lines. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.london_underground/ -""" +"""Sensor for checking the status of London Underground tube lines.""" import logging from datetime import timedelta diff --git a/homeassistant/components/loopenergy/sensor.py b/homeassistant/components/loopenergy/sensor.py index fefba2972f2cc9..23bdf48f64506d 100644 --- a/homeassistant/components/loopenergy/sensor.py +++ b/homeassistant/components/loopenergy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Loop Energy sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.loopenergy/ -""" +"""Support for Loop Energy sensors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/luci/device_tracker.py b/homeassistant/components/luci/device_tracker.py index f60e8edd8c4a5f..df65ed99f295d7 100644 --- a/homeassistant/components/luci/device_tracker.py +++ b/homeassistant/components/luci/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for OpenWRT (luci) routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.luci/ -""" +"""Support for OpenWRT (luci) routers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/lw12wifi/light.py b/homeassistant/components/lw12wifi/light.py index 71716b4130db01..5d9b7635ad2dd1 100644 --- a/homeassistant/components/lw12wifi/light.py +++ b/homeassistant/components/lw12wifi/light.py @@ -1,9 +1,4 @@ -""" -Support for Lagute LW-12 WiFi LED Controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.lw12wifi/ -""" +"""Support for Lagute LW-12 WiFi LED Controller.""" import logging diff --git a/homeassistant/components/lyft/sensor.py b/homeassistant/components/lyft/sensor.py index 6fb4a6bf8bef79..98d79cd970b965 100644 --- a/homeassistant/components/lyft/sensor.py +++ b/homeassistant/components/lyft/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Lyft API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.lyft/ -""" +"""Support for the Lyft API.""" import logging from datetime import timedelta diff --git a/homeassistant/components/magicseaweed/sensor.py b/homeassistant/components/magicseaweed/sensor.py index 0500597b96aed0..4c09d1e09e04f0 100644 --- a/homeassistant/components/magicseaweed/sensor.py +++ b/homeassistant/components/magicseaweed/sensor.py @@ -1,9 +1,4 @@ -""" -Support for magicseaweed data from magicseaweed.com. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.magicseaweed/ -""" +"""Support for magicseaweed data from magicseaweed.com.""" from datetime import timedelta import logging import voluptuous as vol diff --git a/homeassistant/components/manual/alarm_control_panel.py b/homeassistant/components/manual/alarm_control_panel.py index a36a38f596fa6b..14934db41c291f 100644 --- a/homeassistant/components/manual/alarm_control_panel.py +++ b/homeassistant/components/manual/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for manual alarms. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.manual/ -""" +"""Support for manual alarms.""" import copy import datetime import logging diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index 9bee2b81d61201..8057a8993473f1 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for manual alarms controllable via MQTT. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.manual_mqtt/ -""" +"""Support for manual alarms controllable via MQTT.""" import copy import datetime import logging diff --git a/homeassistant/components/marytts/tts.py b/homeassistant/components/marytts/tts.py index f5d19c977a40f2..294383cb4dd99e 100644 --- a/homeassistant/components/marytts/tts.py +++ b/homeassistant/components/marytts/tts.py @@ -1,9 +1,4 @@ -""" -Support for the MaryTTS service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.marytts/ -""" +"""Support for the MaryTTS service.""" import asyncio import logging import re diff --git a/homeassistant/components/mastodon/notify.py b/homeassistant/components/mastodon/notify.py index 6192f6cdca5e30..c1a91b8312ea1f 100644 --- a/homeassistant/components/mastodon/notify.py +++ b/homeassistant/components/mastodon/notify.py @@ -1,9 +1,4 @@ -""" -Mastodon platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.mastodon/ -""" +"""Mastodon platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index ad29a645765bb1..3421551320e203 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with various media players. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/media_player/ -""" +"""Component to interface with various media players.""" import asyncio import base64 import collections diff --git a/homeassistant/components/mediaroom/media_player.py b/homeassistant/components/mediaroom/media_player.py index 29cc7332936057..acbc0462722a59 100644 --- a/homeassistant/components/mediaroom/media_player.py +++ b/homeassistant/components/mediaroom/media_player.py @@ -1,9 +1,4 @@ -""" -Support for the Mediaroom Set-up-box. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.mediaroom/ -""" +"""Support for the Mediaroom Set-up-box.""" import logging import voluptuous as vol diff --git a/homeassistant/components/melissa/climate.py b/homeassistant/components/melissa/climate.py index 0df294a148d9c6..79d94a419912a5 100644 --- a/homeassistant/components/melissa/climate.py +++ b/homeassistant/components/melissa/climate.py @@ -1,9 +1,4 @@ -""" -Support for Melissa Climate A/C. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/climate.melissa/ -""" +"""Support for Melissa Climate A/C.""" import logging from homeassistant.components.climate import ClimateDevice diff --git a/homeassistant/components/message_bird/notify.py b/homeassistant/components/message_bird/notify.py index cfb22ff1d5235b..c801de34a9a6ae 100644 --- a/homeassistant/components/message_bird/notify.py +++ b/homeassistant/components/message_bird/notify.py @@ -1,9 +1,4 @@ -""" -MessageBird platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.message_bird/ -""" +"""MessageBird platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/metoffice/sensor.py b/homeassistant/components/metoffice/sensor.py index 3d9c9485da3fd1..6c4e91517dac6f 100644 --- a/homeassistant/components/metoffice/sensor.py +++ b/homeassistant/components/metoffice/sensor.py @@ -1,9 +1,4 @@ -""" -Support for UK Met Office weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.metoffice/ -""" +"""Support for UK Met Office weather service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/mfi/sensor.py b/homeassistant/components/mfi/sensor.py index 44e5dbda84f410..36f9d1a829c25c 100644 --- a/homeassistant/components/mfi/sensor.py +++ b/homeassistant/components/mfi/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Ubiquiti mFi sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mfi/ -""" +"""Support for Ubiquiti mFi sensors.""" import logging import requests diff --git a/homeassistant/components/mfi/switch.py b/homeassistant/components/mfi/switch.py index 521230ccbd50ff..818081f7a2ed69 100644 --- a/homeassistant/components/mfi/switch.py +++ b/homeassistant/components/mfi/switch.py @@ -1,9 +1,4 @@ -""" -Support for Ubiquiti mFi switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.mfi/ -""" +"""Support for Ubiquiti mFi switches.""" import logging import requests diff --git a/homeassistant/components/mhz19/sensor.py b/homeassistant/components/mhz19/sensor.py index dd8a0395088048..3aa82950fa7b8a 100644 --- a/homeassistant/components/mhz19/sensor.py +++ b/homeassistant/components/mhz19/sensor.py @@ -1,9 +1,4 @@ -""" -Support for CO2 sensor connected to a serial port. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mhz19/ -""" +"""Support for CO2 sensor connected to a serial port.""" import logging from datetime import timedelta diff --git a/homeassistant/components/microsoft/tts.py b/homeassistant/components/microsoft/tts.py index 55cf7a4ae7a016..9fe31ef495eced 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -1,9 +1,4 @@ -""" -Support for the Microsoft Cognitive Services text-to-speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.microsoft/ -""" +"""Support for the Microsoft Cognitive Services text-to-speech service.""" from http.client import HTTPException import logging diff --git a/homeassistant/components/microsoft_face_detect/image_processing.py b/homeassistant/components/microsoft_face_detect/image_processing.py index ae6b9c260cd4da..91eae07e9928bf 100644 --- a/homeassistant/components/microsoft_face_detect/image_processing.py +++ b/homeassistant/components/microsoft_face_detect/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the Microsoft face detect processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.microsoft_face_detect/ -""" +"""Component that will help set the Microsoft face detect processing.""" import logging import voluptuous as vol diff --git a/homeassistant/components/microsoft_face_identify/image_processing.py b/homeassistant/components/microsoft_face_identify/image_processing.py index 7a427d9b046072..52baa3617e82a4 100644 --- a/homeassistant/components/microsoft_face_identify/image_processing.py +++ b/homeassistant/components/microsoft_face_identify/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the Microsoft face for verify processing. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/image_processing.microsoft_face_identify/ -""" +"""Component that will help set the Microsoft face for verify processing.""" import logging import voluptuous as vol diff --git a/homeassistant/components/miflora/sensor.py b/homeassistant/components/miflora/sensor.py index 91f873f5a2d794..04595b0daeb148 100644 --- a/homeassistant/components/miflora/sensor.py +++ b/homeassistant/components/miflora/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Xiaomi Mi Flora BLE plant sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.miflora/ -""" +"""Support for Xiaomi Mi Flora BLE plant sensor.""" from datetime import timedelta import logging import voluptuous as vol diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index c4635f8dc431aa..ed0734588ef035 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Mikrotik routers as device tracker. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.mikrotik/ -""" +"""Support for Mikrotik routers as device tracker.""" import logging import ssl diff --git a/homeassistant/components/mill/climate.py b/homeassistant/components/mill/climate.py index 6867f57ee485ba..cb6d47a52b0826 100644 --- a/homeassistant/components/mill/climate.py +++ b/homeassistant/components/mill/climate.py @@ -1,9 +1,4 @@ -""" -Support for mill wifi-enabled home heaters. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.mill/ -""" +"""Support for mill wifi-enabled home heaters.""" import logging diff --git a/homeassistant/components/min_max/sensor.py b/homeassistant/components/min_max/sensor.py index e18c67471d9fe3..929ef42a2d5475 100644 --- a/homeassistant/components/min_max/sensor.py +++ b/homeassistant/components/min_max/sensor.py @@ -1,9 +1,4 @@ -""" -Support for displaying the minimal and the maximal value. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.min_max/ -""" +"""Support for displaying the minimal and the maximal value.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mitemp_bt/sensor.py b/homeassistant/components/mitemp_bt/sensor.py index f8bee17978d156..cea2c6a55dbd6f 100644 --- a/homeassistant/components/mitemp_bt/sensor.py +++ b/homeassistant/components/mitemp_bt/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Xiaomi Mi Temp BLE environmental sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mitemp_bt/ -""" +"""Support for Xiaomi Mi Temp BLE environmental sensor.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mjpeg/camera.py b/homeassistant/components/mjpeg/camera.py index 0e73dfac0b7e5c..b9aa9c3e186163 100644 --- a/homeassistant/components/mjpeg/camera.py +++ b/homeassistant/components/mjpeg/camera.py @@ -1,9 +1,4 @@ -""" -Support for IP Cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.mjpeg/ -""" +"""Support for IP Cameras.""" import asyncio import logging from contextlib import closing diff --git a/homeassistant/components/modem_callerid/sensor.py b/homeassistant/components/modem_callerid/sensor.py index 2da7953c12a2b0..b87f4840334d4f 100644 --- a/homeassistant/components/modem_callerid/sensor.py +++ b/homeassistant/components/modem_callerid/sensor.py @@ -1,9 +1,4 @@ -""" -A sensor to monitor incoming calls using a USB modem that supports caller ID. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.modem_callerid/ -""" +"""A sensor for incoming calls using a USB modem that supports caller ID.""" import logging import voluptuous as vol from homeassistant.const import (STATE_IDLE, diff --git a/homeassistant/components/mold_indicator/sensor.py b/homeassistant/components/mold_indicator/sensor.py index 2a250f0e63d6ba..645e04a890c800 100644 --- a/homeassistant/components/mold_indicator/sensor.py +++ b/homeassistant/components/mold_indicator/sensor.py @@ -1,9 +1,4 @@ -""" -Calculates mold growth indication from temperature and humidity. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mold_indicator/ -""" +"""Calculates mold growth indication from temperature and humidity.""" import logging import math diff --git a/homeassistant/components/monoprice/media_player.py b/homeassistant/components/monoprice/media_player.py index e98ad47a6e7fa1..edffd6ac7ce825 100644 --- a/homeassistant/components/monoprice/media_player.py +++ b/homeassistant/components/monoprice/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with Monoprice 6 zone home audio controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.monoprice/ -""" +"""Support for interfacing with Monoprice 6 zone home audio controller.""" import logging import voluptuous as vol diff --git a/homeassistant/components/moon/sensor.py b/homeassistant/components/moon/sensor.py index a019d21fd784df..3b1d70bc731213 100644 --- a/homeassistant/components/moon/sensor.py +++ b/homeassistant/components/moon/sensor.py @@ -1,9 +1,4 @@ -""" -Support for tracking the moon phases. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.moon/ -""" +"""Support for tracking the moon phases.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mpchc/media_player.py b/homeassistant/components/mpchc/media_player.py index 61d89c6d0b15dc..54518667949f09 100644 --- a/homeassistant/components/mpchc/media_player.py +++ b/homeassistant/components/mpchc/media_player.py @@ -1,9 +1,4 @@ -""" -Support to interface with the MPC-HC Web API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.mpchc/ -""" +"""Support to interface with the MPC-HC Web API.""" import logging import re diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index 9d8015109b2007..8cbc1406e0bc4a 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -1,9 +1,4 @@ -""" -Support to interact with a Music Player Daemon. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.mpd/ -""" +"""Support to interact with a Music Player Daemon.""" from datetime import timedelta import logging import os diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 2c605fb4b0ddd6..3f1f8617689c58 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -1,9 +1,4 @@ -""" -Support for MQTT message handling. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/mqtt/ -""" +"""Support for MQTT message handling.""" import asyncio from functools import partial, wraps import inspect diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index f1142baa37aac6..d30c91bb9b2163 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -This platform enables the possibility to control a MQTT alarm. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.mqtt/ -""" +"""This platform enables the possibility to control a MQTT alarm.""" import logging import re diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 7532f3053281fb..f942091984a24b 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for MQTT binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.mqtt/ -""" +"""Support for MQTT binary sensors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 889e5533403316..34e83a51f28eeb 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -1,9 +1,4 @@ -""" -Camera that loads a picture from an MQTT topic. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.mqtt/ -""" +"""Camera that loads a picture from an MQTT topic.""" import asyncio import logging diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index a9c23d27e11d0e..52d18a034194ca 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -1,9 +1,4 @@ -""" -Support for MQTT climate devices. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/climate.mqtt/ -""" +"""Support for MQTT climate devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 8116421ac10625..08b6c2b74ba0cd 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -1,9 +1,4 @@ -""" -Support for MQTT cover devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover.mqtt/ -""" +"""Support for MQTT cover devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index 0f22ed150ca630..659c6315b2150a 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for tracking MQTT enabled devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.mqtt/ -""" +"""Support for tracking MQTT enabled devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index cb87a208b4fef3..b10e05cbf0f262 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -1,9 +1,4 @@ -""" -Support for MQTT discovery. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/mqtt/#discovery -""" +"""Support for MQTT discovery.""" import asyncio import json import logging diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index b8bff6088d8cca..7dff81160e0f83 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,9 +1,4 @@ -""" -Support for MQTT fans. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/fan.mqtt/ -""" +"""Support for MQTT fans.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index ee459d2174f001..e01a30f0fab065 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -1,9 +1,4 @@ -""" -Support for MQTT locks. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/lock.mqtt/ -""" +"""Support for MQTT locks.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index aa8d5e2a31e348..1e024fdc768f4e 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -1,9 +1,4 @@ -""" -Support for MQTT sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mqtt/ -""" +"""Support for MQTT sensors.""" from datetime import timedelta import json import logging diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index 3373149a013fab..d7d36add517f6e 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -1,9 +1,4 @@ -""" -Support for a local MQTT broker. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/mqtt/#use-the-embedded-broker -""" +"""Support for a local MQTT broker.""" import asyncio import logging import tempfile diff --git a/homeassistant/components/mqtt/subscription.py b/homeassistant/components/mqtt/subscription.py index e159132d5609cd..368a12c1956728 100644 --- a/homeassistant/components/mqtt/subscription.py +++ b/homeassistant/components/mqtt/subscription.py @@ -1,9 +1,4 @@ -""" -Helper to handle a set of topics to subscribe to. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/mqtt/ -""" +"""Helper to handle a set of topics to subscribe to.""" import logging from typing import Any, Callable, Dict, Optional diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index 4847afd80c90e6..acfcf3de01c430 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -1,9 +1,4 @@ -""" -Support for MQTT switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.mqtt/ -""" +"""Support for MQTT switches.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index efa00821c1ba62..63a764e874663f 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -1,9 +1,4 @@ -""" -Support for a generic MQTT vacuum. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/vacuum.mqtt/ -""" +"""Support for a generic MQTT vacuum.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mqtt_json/device_tracker.py b/homeassistant/components/mqtt_json/device_tracker.py index 0a1b327dca9bc3..6059b26bcbdc11 100644 --- a/homeassistant/components/mqtt_json/device_tracker.py +++ b/homeassistant/components/mqtt_json/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for GPS tracking MQTT enabled devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.mqtt_json/ -""" +"""Support for GPS tracking MQTT enabled devices.""" import json import logging diff --git a/homeassistant/components/mqtt_room/sensor.py b/homeassistant/components/mqtt_room/sensor.py index 36f99719da43ba..961769711a4d6c 100644 --- a/homeassistant/components/mqtt_room/sensor.py +++ b/homeassistant/components/mqtt_room/sensor.py @@ -1,9 +1,4 @@ -""" -Support for MQTT room presence detection. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mqtt_room/ -""" +"""Support for MQTT room presence detection.""" import logging import json from datetime import timedelta diff --git a/homeassistant/components/mvglive/sensor.py b/homeassistant/components/mvglive/sensor.py index 71690f643f47b1..978c9ad34eb436 100644 --- a/homeassistant/components/mvglive/sensor.py +++ b/homeassistant/components/mvglive/sensor.py @@ -1,9 +1,4 @@ -""" -Support for real-time departure information for public transport in Munich. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.mvglive/ -""" +"""Support for departure information for public transport in Munich.""" import logging from datetime import timedelta diff --git a/homeassistant/components/mycroft/notify.py b/homeassistant/components/mycroft/notify.py index a8a401a9c1f054..d66be629f17b09 100644 --- a/homeassistant/components/mycroft/notify.py +++ b/homeassistant/components/mycroft/notify.py @@ -1,9 +1,4 @@ -""" -Mycroft AI notification platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.mycroft/ -""" +"""Mycroft AI notification platform.""" import logging from homeassistant.components.notify import BaseNotificationService diff --git a/homeassistant/components/myq/cover.py b/homeassistant/components/myq/cover.py index b2587c0651262c..b1112f153b23a8 100644 --- a/homeassistant/components/myq/cover.py +++ b/homeassistant/components/myq/cover.py @@ -1,9 +1,4 @@ -""" -Support for MyQ-Enabled Garage Doors. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/cover.myq/ -""" +"""Support for MyQ-Enabled Garage Doors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/mystrom/binary_sensor.py b/homeassistant/components/mystrom/binary_sensor.py index 4927be27eb303f..42245dc4df3e85 100644 --- a/homeassistant/components/mystrom/binary_sensor.py +++ b/homeassistant/components/mystrom/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for the myStrom buttons. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.mystrom/ -""" +"""Support for the myStrom buttons.""" import logging from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice diff --git a/homeassistant/components/nad/media_player.py b/homeassistant/components/nad/media_player.py index 00738abe4d1472..8c5a14a35243c8 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with NAD receivers through RS-232. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.nad/ -""" +"""Support for interfacing with NAD receivers through RS-232.""" import logging import voluptuous as vol diff --git a/homeassistant/components/nanoleaf/light.py b/homeassistant/components/nanoleaf/light.py index 818617f1b9adf6..60457e21f9a67d 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -1,9 +1,4 @@ -""" -Support for Nanoleaf Lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.nanoleaf/ -""" +"""Support for Nanoleaf Lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/nederlandse_spoorwegen/sensor.py b/homeassistant/components/nederlandse_spoorwegen/sensor.py index 5d9376ad9ebe07..224d16e4869b9a 100644 --- a/homeassistant/components/nederlandse_spoorwegen/sensor.py +++ b/homeassistant/components/nederlandse_spoorwegen/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Nederlandse Spoorwegen public transport. - -For more details on this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.nederlandse_spoorwegen/ -""" +"""Support for Nederlandse Spoorwegen public transport.""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/nello/lock.py b/homeassistant/components/nello/lock.py index e7eaea8fcd33b1..efb7719e2013fd 100644 --- a/homeassistant/components/nello/lock.py +++ b/homeassistant/components/nello/lock.py @@ -1,9 +1,4 @@ -""" -Nello.io lock platform. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/lock.nello/ -""" +"""Nello.io lock platform.""" from itertools import filterfalse import logging diff --git a/homeassistant/components/ness_alarm/alarm_control_panel.py b/homeassistant/components/ness_alarm/alarm_control_panel.py index f77b534980f4fe..618297ef9a5216 100644 --- a/homeassistant/components/ness_alarm/alarm_control_panel.py +++ b/homeassistant/components/ness_alarm/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for Ness D8X/D16X alarm panel. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.ness_alarm/ -""" +"""Support for Ness D8X/D16X alarm panel.""" import logging diff --git a/homeassistant/components/ness_alarm/binary_sensor.py b/homeassistant/components/ness_alarm/binary_sensor.py index 7b684f74aa1253..2bed9eb64042bf 100644 --- a/homeassistant/components/ness_alarm/binary_sensor.py +++ b/homeassistant/components/ness_alarm/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Ness D8X/D16X zone states - represented as binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.ness_alarm/ -""" +"""Support for Ness D8X/D16X zone states - represented as binary sensors.""" import logging from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/netatmo_public/sensor.py b/homeassistant/components/netatmo_public/sensor.py index 7a500b6618385f..3480534436da6b 100644 --- a/homeassistant/components/netatmo_public/sensor.py +++ b/homeassistant/components/netatmo_public/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Sensors using public Netatmo data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.netatmo_public/. -""" +"""Support for Sensors using public Netatmo data.""" from datetime import timedelta import logging diff --git a/homeassistant/components/netdata/sensor.py b/homeassistant/components/netdata/sensor.py index 6a6eea020059af..6d99722a4162d4 100644 --- a/homeassistant/components/netdata/sensor.py +++ b/homeassistant/components/netdata/sensor.py @@ -1,9 +1,4 @@ -""" -Support gathering system information of hosts which are running netdata. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.netdata/ -""" +"""Support gathering system information of hosts which are running netdata.""" from datetime import timedelta import logging diff --git a/homeassistant/components/netgear/device_tracker.py b/homeassistant/components/netgear/device_tracker.py index 49bce932159546..ce8c2d6066d3d4 100644 --- a/homeassistant/components/netgear/device_tracker.py +++ b/homeassistant/components/netgear/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Netgear routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.netgear/ -""" +"""Support for Netgear routers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/netio/switch.py b/homeassistant/components/netio/switch.py index 4492697406de9a..27a7dfbd5e7de5 100644 --- a/homeassistant/components/netio/switch.py +++ b/homeassistant/components/netio/switch.py @@ -1,9 +1,4 @@ -""" -The Netio switch component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.netio/ -""" +"""The Netio switch component.""" import logging from collections import namedtuple from datetime import timedelta diff --git a/homeassistant/components/neurio_energy/sensor.py b/homeassistant/components/neurio_energy/sensor.py index 673cd8da724cf7..9e12465c69b078 100644 --- a/homeassistant/components/neurio_energy/sensor.py +++ b/homeassistant/components/neurio_energy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring a Neurio energy sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.neurio_energy/ -""" +"""Support for monitoring a Neurio energy sensor.""" import logging from datetime import timedelta diff --git a/homeassistant/components/nfandroidtv/notify.py b/homeassistant/components/nfandroidtv/notify.py index c4003a6312a523..1cf1fbd0dbc708 100644 --- a/homeassistant/components/nfandroidtv/notify.py +++ b/homeassistant/components/nfandroidtv/notify.py @@ -1,9 +1,4 @@ -""" -Notifications for Android TV notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.nfandroidtv/ -""" +"""Notifications for Android TV notification service.""" import base64 import io import logging diff --git a/homeassistant/components/niko_home_control/light.py b/homeassistant/components/niko_home_control/light.py index 6b58ced59897f8..00e8dc838a6c0d 100644 --- a/homeassistant/components/niko_home_control/light.py +++ b/homeassistant/components/niko_home_control/light.py @@ -1,9 +1,4 @@ -""" -Support for Niko Home Control. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/light.niko_home_control/ -""" +"""Support for Niko Home Control.""" import logging import voluptuous as vol diff --git a/homeassistant/components/nilu/air_quality.py b/homeassistant/components/nilu/air_quality.py index 2ab38c1ad95bd3..979d5736d6a20d 100644 --- a/homeassistant/components/nilu/air_quality.py +++ b/homeassistant/components/nilu/air_quality.py @@ -1,9 +1,4 @@ -""" -Sensor for checking the air quality around Norway. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/air_quality.nilu/ -""" +"""Sensor for checking the air quality around Norway.""" from datetime import timedelta import logging diff --git a/homeassistant/components/nmbs/sensor.py b/homeassistant/components/nmbs/sensor.py index 15f29339087465..034c37530b355e 100644 --- a/homeassistant/components/nmbs/sensor.py +++ b/homeassistant/components/nmbs/sensor.py @@ -1,9 +1,4 @@ -""" -Get ride details and liveboard details for NMBS (Belgian railway). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.nmbs/ -""" +"""Get ride details and liveboard details for NMBS (Belgian railway).""" import logging import voluptuous as vol diff --git a/homeassistant/components/noaa_tides/sensor.py b/homeassistant/components/noaa_tides/sensor.py index 6a72fdf8f2afa3..0c4bde94f5770f 100644 --- a/homeassistant/components/noaa_tides/sensor.py +++ b/homeassistant/components/noaa_tides/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the NOAA Tides and Currents API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.noaa_tides/ -""" +"""Support for the NOAA Tides and Currents API.""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/norway_air/air_quality.py b/homeassistant/components/norway_air/air_quality.py index 712f2734ea8c66..06ed68801f89c7 100644 --- a/homeassistant/components/norway_air/air_quality.py +++ b/homeassistant/components/norway_air/air_quality.py @@ -1,9 +1,4 @@ -""" -Sensor for checking the air quality forecast around Norway. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/air_quality.norway_air/ -""" +"""Sensor for checking the air quality forecast around Norway.""" import logging from datetime import timedelta diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index f0320617e191be..8bb3384aebd9db 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -1,9 +1,4 @@ -""" -Provides functionality to notify people. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/notify/ -""" +"""Provides functionality to notify people.""" import asyncio import logging from functools import partial diff --git a/homeassistant/components/nsw_fuel_station/sensor.py b/homeassistant/components/nsw_fuel_station/sensor.py index f0da619a6e7c7b..ce4337fc93ab6c 100644 --- a/homeassistant/components/nsw_fuel_station/sensor.py +++ b/homeassistant/components/nsw_fuel_station/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor platform to display the current fuel prices at a NSW fuel station. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.nsw_fuel_station/ -""" +"""Sensor platform to display the current fuel prices at a NSW fuel station.""" import datetime import logging from typing import Optional diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index 27e909f8f04e2c..32adc1d216f3c5 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -1,9 +1,4 @@ -""" -Support for NuHeat thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.nuheat/ -""" +"""Support for NuHeat thermostats.""" from datetime import timedelta import logging diff --git a/homeassistant/components/nuki/lock.py b/homeassistant/components/nuki/lock.py index 8af798e31e0267..ef49d4b97dd955 100644 --- a/homeassistant/components/nuki/lock.py +++ b/homeassistant/components/nuki/lock.py @@ -1,9 +1,4 @@ -""" -Nuki.io lock platform. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/lock.nuki/ -""" +"""Nuki.io lock platform.""" from datetime import timedelta import logging diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 1464c0d91c1fab..43ba06f70eb1b3 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -1,9 +1,4 @@ -""" -Provides a sensor to track various status aspects of a UPS. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.nut/ -""" +"""Provides a sensor to track various status aspects of a UPS.""" import logging from datetime import timedelta diff --git a/homeassistant/components/nx584/alarm_control_panel.py b/homeassistant/components/nx584/alarm_control_panel.py index c84872d0b257da..c5e1fede6fd8fd 100644 --- a/homeassistant/components/nx584/alarm_control_panel.py +++ b/homeassistant/components/nx584/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for NX584 alarm control panels. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.nx584/ -""" +"""Support for NX584 alarm control panels.""" import logging import requests diff --git a/homeassistant/components/nx584/binary_sensor.py b/homeassistant/components/nx584/binary_sensor.py index 2929acc27095f8..61f8fb801eac5b 100644 --- a/homeassistant/components/nx584/binary_sensor.py +++ b/homeassistant/components/nx584/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for exposing NX584 elements as sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.nx584/ -""" +"""Support for exposing NX584 elements as sensors.""" import logging import threading import time diff --git a/homeassistant/components/nzbget/sensor.py b/homeassistant/components/nzbget/sensor.py index 2ee8e37a57ae47..bb0b7c912fd965 100644 --- a/homeassistant/components/nzbget/sensor.py +++ b/homeassistant/components/nzbget/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring NZBGet NZB client. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.nzbget/ -""" +"""Support for monitoring NZBGet NZB client.""" from datetime import timedelta import logging diff --git a/homeassistant/components/ohmconnect/sensor.py b/homeassistant/components/ohmconnect/sensor.py index 3487cab2fcd3d0..1d870e4d15a12c 100644 --- a/homeassistant/components/ohmconnect/sensor.py +++ b/homeassistant/components/ohmconnect/sensor.py @@ -1,9 +1,4 @@ -""" -Support for OhmConnect. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/sensor.ohmconnect/ -""" +"""Support for OhmConnect.""" import logging from datetime import timedelta diff --git a/homeassistant/components/onewire/sensor.py b/homeassistant/components/onewire/sensor.py index d39ab24230cb6d..2e55b5cea3605f 100644 --- a/homeassistant/components/onewire/sensor.py +++ b/homeassistant/components/onewire/sensor.py @@ -1,9 +1,4 @@ -""" -Support for 1-Wire environment sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.onewire/ -""" +"""Support for 1-Wire environment sensors.""" import os import time import logging diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index 2fb284bb24a6a3..64b9684c58c4ec 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Onkyo Receivers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.onkyo/ -""" +"""Support for Onkyo Receivers.""" import logging # pylint: disable=unused-import diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index a196f87cd10f2e..90222b9cafc06b 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -1,9 +1,4 @@ -""" -Support for ONVIF Cameras with FFmpeg as decoder. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.onvif/ -""" +"""Support for ONVIF Cameras with FFmpeg as decoder.""" import asyncio import logging import os diff --git a/homeassistant/components/openalpr_cloud/image_processing.py b/homeassistant/components/openalpr_cloud/image_processing.py index c983a9455486f2..12146009fac1f6 100644 --- a/homeassistant/components/openalpr_cloud/image_processing.py +++ b/homeassistant/components/openalpr_cloud/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the OpenALPR cloud for ALPR processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.openalpr_cloud/ -""" +"""Component that will help set the OpenALPR cloud for ALPR processing.""" import asyncio import logging from base64 import b64encode diff --git a/homeassistant/components/openalpr_local/image_processing.py b/homeassistant/components/openalpr_local/image_processing.py index 4a98594d50c199..811a160dd02bcf 100644 --- a/homeassistant/components/openalpr_local/image_processing.py +++ b/homeassistant/components/openalpr_local/image_processing.py @@ -1,9 +1,4 @@ -""" -Component that will help set the OpenALPR local for ALPR processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.openalpr_local/ -""" +"""Component that will help set the OpenALPR local for ALPR processing.""" import asyncio import io import logging diff --git a/homeassistant/components/openevse/sensor.py b/homeassistant/components/openevse/sensor.py index cf41f87718d62e..e54b47236c5fe3 100644 --- a/homeassistant/components/openevse/sensor.py +++ b/homeassistant/components/openevse/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring an OpenEVSE Charger. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.openevse/ -""" +"""Support for monitoring an OpenEVSE Charger.""" import logging from requests import RequestException diff --git a/homeassistant/components/openexchangerates/sensor.py b/homeassistant/components/openexchangerates/sensor.py index 6361b823dea055..6c146044140c69 100644 --- a/homeassistant/components/openexchangerates/sensor.py +++ b/homeassistant/components/openexchangerates/sensor.py @@ -1,9 +1,4 @@ -""" -Support for openexchangerates.org exchange rates service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.openexchangerates/ -""" +"""Support for openexchangerates.org exchange rates service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/opengarage/cover.py b/homeassistant/components/opengarage/cover.py index 664d2e291ac6d3..1e3d3128829ee4 100644 --- a/homeassistant/components/opengarage/cover.py +++ b/homeassistant/components/opengarage/cover.py @@ -1,9 +1,4 @@ -""" -Platform for the opengarage.io cover component. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/cover.opengarage/ -""" +"""Platform for the opengarage.io cover component.""" import logging import requests diff --git a/homeassistant/components/openhardwaremonitor/sensor.py b/homeassistant/components/openhardwaremonitor/sensor.py index d429cad9d9759b..7c5072db97c3f1 100644 --- a/homeassistant/components/openhardwaremonitor/sensor.py +++ b/homeassistant/components/openhardwaremonitor/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Open Hardware Monitor Sensor Platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.openhardwaremonitor/ -""" +"""Support for Open Hardware Monitor Sensor Platform.""" from datetime import timedelta import logging diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index d828284a563e24..03926bce8c5ce8 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Openhome Devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.openhome/ -""" +"""Support for Openhome Devices.""" import logging from homeassistant.components.media_player import ( diff --git a/homeassistant/components/opensky/sensor.py b/homeassistant/components/opensky/sensor.py index 5ee11af4e605a9..3019c54471f875 100644 --- a/homeassistant/components/opensky/sensor.py +++ b/homeassistant/components/opensky/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for the Open Sky Network. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.opensky/ -""" +"""Sensor for the Open Sky Network.""" import logging from datetime import timedelta diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index a137836138b2ab..5de67721e3057f 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the OpenWeatherMap (OWM) service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.openweathermap/ -""" +"""Support for the OpenWeatherMap (OWM) service.""" from datetime import timedelta import logging diff --git a/homeassistant/components/opple/light.py b/homeassistant/components/opple/light.py index fb503d33d31c9d..03e36dc179d43b 100644 --- a/homeassistant/components/opple/light.py +++ b/homeassistant/components/opple/light.py @@ -1,9 +1,4 @@ -""" -Support for the Opple light. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.opple/ -""" +"""Support for the Opple light.""" import logging diff --git a/homeassistant/components/orvibo/switch.py b/homeassistant/components/orvibo/switch.py index 9e6686ca3aef7c..c77e24446ec2a6 100644 --- a/homeassistant/components/orvibo/switch.py +++ b/homeassistant/components/orvibo/switch.py @@ -1,9 +1,4 @@ -""" -Support for Orvibo S20 Wifi Smart Switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.orvibo/ -""" +"""Support for Orvibo S20 Wifi Smart Switches.""" import logging import voluptuous as vol diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index 81b8e2a88ec296..b880273fd1e916 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -1,9 +1,4 @@ -""" -Support for Osram Lightify. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.osramlightify/ -""" +"""Support for Osram Lightify.""" import logging import random import socket diff --git a/homeassistant/components/otp/sensor.py b/homeassistant/components/otp/sensor.py index 5394b49c389109..2ac4c51998443e 100644 --- a/homeassistant/components/otp/sensor.py +++ b/homeassistant/components/otp/sensor.py @@ -1,9 +1,4 @@ -""" -Support for One-Time Password (OTP). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.otp/ -""" +"""Support for One-Time Password (OTP).""" import time import logging diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index f1214b62b0edc0..69ea723d84c503 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -1,9 +1,4 @@ -""" -Device tracker platform that adds support for OwnTracks over MQTT. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.owntracks/ -""" +"""Device tracker platform that adds support for OwnTracks over MQTT.""" import json import logging diff --git a/homeassistant/components/panasonic_bluray/media_player.py b/homeassistant/components/panasonic_bluray/media_player.py index 36a3160d3b52fa..ebf71135d343f7 100644 --- a/homeassistant/components/panasonic_bluray/media_player.py +++ b/homeassistant/components/panasonic_bluray/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Panasonic Blu-Ray players. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/panasonic_bluray/ -""" +"""Support for Panasonic Blu-Ray players.""" from datetime import timedelta import logging diff --git a/homeassistant/components/panasonic_viera/media_player.py b/homeassistant/components/panasonic_viera/media_player.py index f1ac0cd90d461a..324becd0bf756d 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with a Panasonic Viera TV. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.panasonic_viera/ -""" +"""Support for interface with a Panasonic Viera TV.""" import logging import voluptuous as vol diff --git a/homeassistant/components/pandora/media_player.py b/homeassistant/components/pandora/media_player.py index ca78f7a43182b1..32cde430d0e97b 100644 --- a/homeassistant/components/pandora/media_player.py +++ b/homeassistant/components/pandora/media_player.py @@ -1,9 +1,4 @@ -""" -Component for controlling Pandora stations through the pianobar client. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/media_player.pandora/ -""" +"""Component for controlling Pandora stations through the pianobar client.""" from datetime import timedelta import logging import os diff --git a/homeassistant/components/philips_js/media_player.py b/homeassistant/components/philips_js/media_player.py index 97ec758e6cf26d..f5eddff8d138dc 100644 --- a/homeassistant/components/philips_js/media_player.py +++ b/homeassistant/components/philips_js/media_player.py @@ -1,9 +1,4 @@ -""" -Media Player component to integrate TVs exposing the Joint Space API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.philips_js/ -""" +"""Media Player component to integrate TVs exposing the Joint Space API.""" from datetime import timedelta import logging diff --git a/homeassistant/components/pi_hole/sensor.py b/homeassistant/components/pi_hole/sensor.py index ae9aca5bc795d1..805e17ebdff418 100644 --- a/homeassistant/components/pi_hole/sensor.py +++ b/homeassistant/components/pi_hole/sensor.py @@ -1,9 +1,4 @@ -""" -Support for getting statistical data from a Pi-hole system. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pi_hole/ -""" +"""Support for getting statistical data from a Pi-hole system.""" from datetime import timedelta import logging diff --git a/homeassistant/components/picotts/tts.py b/homeassistant/components/picotts/tts.py index c164e7fb85dae7..fffadae0f13485 100644 --- a/homeassistant/components/picotts/tts.py +++ b/homeassistant/components/picotts/tts.py @@ -1,9 +1,4 @@ -""" -Support for the Pico TTS speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.picotts/ -""" +"""Support for the Pico TTS speech service.""" import logging import os import shutil diff --git a/homeassistant/components/piglow/light.py b/homeassistant/components/piglow/light.py index 56c72e01fdf7c2..dc3906b20026cd 100644 --- a/homeassistant/components/piglow/light.py +++ b/homeassistant/components/piglow/light.py @@ -1,9 +1,4 @@ -""" -Support for Piglow LED's. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.piglow/ -""" +"""Support for Piglow LED's.""" import logging import subprocess diff --git a/homeassistant/components/pilight/__init__.py b/homeassistant/components/pilight/__init__.py index d307a428e0e27d..46be3b3720438b 100644 --- a/homeassistant/components/pilight/__init__.py +++ b/homeassistant/components/pilight/__init__.py @@ -1,9 +1,4 @@ -""" -Component to create an interface to a Pilight daemon. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/pilight/ -""" +"""Component to create an interface to a Pilight daemon.""" import logging import functools import socket diff --git a/homeassistant/components/pilight/binary_sensor.py b/homeassistant/components/pilight/binary_sensor.py index de23baef88475f..131a91b5fc3c0a 100644 --- a/homeassistant/components/pilight/binary_sensor.py +++ b/homeassistant/components/pilight/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Pilight binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.pilight/ -""" +"""Support for Pilight binary sensors.""" import datetime import logging diff --git a/homeassistant/components/pilight/sensor.py b/homeassistant/components/pilight/sensor.py index ddcbe018f8e4ab..c36151c90dce26 100644 --- a/homeassistant/components/pilight/sensor.py +++ b/homeassistant/components/pilight/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Pilight sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pilight/ -""" +"""Support for Pilight sensors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/pilight/switch.py b/homeassistant/components/pilight/switch.py index 3bbe2e69110180..d645d8e3013ce7 100644 --- a/homeassistant/components/pilight/switch.py +++ b/homeassistant/components/pilight/switch.py @@ -1,9 +1,4 @@ -""" -Support for switching devices via Pilight to on and off. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.pilight/ -""" +"""Support for switching devices via Pilight to on and off.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ping/binary_sensor.py b/homeassistant/components/ping/binary_sensor.py index 4c597dd63e1e34..4f95a470efb75e 100644 --- a/homeassistant/components/ping/binary_sensor.py +++ b/homeassistant/components/ping/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Tracks the latency of a host by sending ICMP echo requests (ping). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.ping/ -""" +"""Tracks the latency of a host by sending ICMP echo requests (ping).""" import logging import subprocess import re diff --git a/homeassistant/components/ping/device_tracker.py b/homeassistant/components/ping/device_tracker.py index f3492da9e80b59..9f9bf4475b4a27 100644 --- a/homeassistant/components/ping/device_tracker.py +++ b/homeassistant/components/ping/device_tracker.py @@ -1,9 +1,4 @@ -""" -Tracks devices by sending a ICMP echo request (ping). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ping/ -""" +"""Tracks devices by sending a ICMP echo request (ping).""" import logging import subprocess import sys diff --git a/homeassistant/components/pioneer/media_player.py b/homeassistant/components/pioneer/media_player.py index 00fa453100ae7e..a687ba5ad4a813 100644 --- a/homeassistant/components/pioneer/media_player.py +++ b/homeassistant/components/pioneer/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Pioneer Network Receivers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.pioneer/ -""" +"""Support for Pioneer Network Receivers.""" import logging import telnetlib diff --git a/homeassistant/components/pjlink/media_player.py b/homeassistant/components/pjlink/media_player.py index c1b883a0295893..ad7bdc9e77cb20 100644 --- a/homeassistant/components/pjlink/media_player.py +++ b/homeassistant/components/pjlink/media_player.py @@ -1,9 +1,4 @@ -""" -Support for controlling projector via the PJLink protocol. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.pjlink/ -""" +"""Support for controlling projector via the PJLink protocol.""" import logging import voluptuous as vol diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index a68a2faade818e..f2af6836e3be4c 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -1,9 +1,4 @@ -""" -Support to interface with the Plex API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.plex/ -""" +"""Support to interface with the Plex API.""" from datetime import timedelta import json import logging diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index eaf73ceb566ff9..a3df6fdb41e8fd 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Plex media server monitoring. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.plex/ -""" +"""Support for Plex media server monitoring.""" from datetime import timedelta import logging import voluptuous as vol diff --git a/homeassistant/components/pocketcasts/sensor.py b/homeassistant/components/pocketcasts/sensor.py index 9d5b837bba96ea..f09e90120049d9 100644 --- a/homeassistant/components/pocketcasts/sensor.py +++ b/homeassistant/components/pocketcasts/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Pocket Casts. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pocketcasts/ -""" +"""Support for Pocket Casts.""" import logging from datetime import timedelta diff --git a/homeassistant/components/postnl/sensor.py b/homeassistant/components/postnl/sensor.py index 84cb42c09574f1..f9c8019cd31a00 100644 --- a/homeassistant/components/postnl/sensor.py +++ b/homeassistant/components/postnl/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for PostNL packages. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.postnl/ -""" +"""Sensor for PostNL packages.""" from datetime import timedelta import logging diff --git a/homeassistant/components/prezzibenzina/sensor.py b/homeassistant/components/prezzibenzina/sensor.py index 171fea5331485b..525de7dad2f837 100644 --- a/homeassistant/components/prezzibenzina/sensor.py +++ b/homeassistant/components/prezzibenzina/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the PrezziBenzina.it service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.prezzibenzina/ -""" +"""Support for the PrezziBenzina.it service.""" import datetime as dt from datetime import timedelta import logging diff --git a/homeassistant/components/proliphix/climate.py b/homeassistant/components/proliphix/climate.py index c88ece033df43c..c165334201da2f 100644 --- a/homeassistant/components/proliphix/climate.py +++ b/homeassistant/components/proliphix/climate.py @@ -1,9 +1,4 @@ -""" -Support for Proliphix NT10e Thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.proliphix/ -""" +"""Support for Proliphix NT10e Thermostats.""" import voluptuous as vol from homeassistant.components.climate import ClimateDevice, PLATFORM_SCHEMA diff --git a/homeassistant/components/prowl/notify.py b/homeassistant/components/prowl/notify.py index 6d911789121ba1..1f2067cc6600c4 100644 --- a/homeassistant/components/prowl/notify.py +++ b/homeassistant/components/prowl/notify.py @@ -1,9 +1,4 @@ -""" -Prowl notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.prowl/ -""" +"""Prowl notification service.""" import asyncio import logging diff --git a/homeassistant/components/proxy/camera.py b/homeassistant/components/proxy/camera.py index 3e6e4911d27b2b..fda2cdea60ef8e 100644 --- a/homeassistant/components/proxy/camera.py +++ b/homeassistant/components/proxy/camera.py @@ -1,9 +1,4 @@ -""" -Proxy camera platform that enables image processing of camera data. - -For more details about this platform, please refer to the documentation -https://www.home-assistant.io/components/camera.proxy/ -""" +"""Proxy camera platform that enables image processing of camera data.""" import asyncio import logging diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index 9183bbe198901d..191eb223707d99 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -1,9 +1,4 @@ -""" -Support for PlayStation 4 consoles. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/ps4/ -""" +"""Support for PlayStation 4 consoles.""" import logging from homeassistant.const import CONF_REGION diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index 80c1fda52de055..4dc4fa0a317089 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -1,9 +1,4 @@ -""" -Support for PlayStation 4 consoles. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/ps4/ -""" +"""Support for PlayStation 4 consoles.""" import logging import socket diff --git a/homeassistant/components/pulseaudio_loopback/switch.py b/homeassistant/components/pulseaudio_loopback/switch.py index f112608d760e29..9ec6587f678458 100644 --- a/homeassistant/components/pulseaudio_loopback/switch.py +++ b/homeassistant/components/pulseaudio_loopback/switch.py @@ -1,9 +1,4 @@ -""" -Switch logic for loading/unloading pulseaudio loopback modules. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.pulseaudio_loopback/ -""" +"""Switch logic for loading/unloading pulseaudio loopback modules.""" import logging import re import socket diff --git a/homeassistant/components/push/camera.py b/homeassistant/components/push/camera.py index 5490cd1508cbd8..c0424f15898e58 100644 --- a/homeassistant/components/push/camera.py +++ b/homeassistant/components/push/camera.py @@ -1,9 +1,4 @@ -""" -Camera platform that receives images through HTTP POST. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/camera.push/ -""" +"""Camera platform that receives images through HTTP POST.""" import logging import asyncio diff --git a/homeassistant/components/pushbullet/notify.py b/homeassistant/components/pushbullet/notify.py index f0b4ec24da8a4b..3fc90161ae0cd9 100644 --- a/homeassistant/components/pushbullet/notify.py +++ b/homeassistant/components/pushbullet/notify.py @@ -1,9 +1,4 @@ -""" -Pushbullet platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.pushbullet/ -""" +"""Pushbullet platform for notify component.""" import logging import mimetypes diff --git a/homeassistant/components/pushbullet/sensor.py b/homeassistant/components/pushbullet/sensor.py index 9b26bdfbbe9c51..c90f952e7de0d3 100644 --- a/homeassistant/components/pushbullet/sensor.py +++ b/homeassistant/components/pushbullet/sensor.py @@ -1,9 +1,4 @@ -""" -Pushbullet platform for sensor component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pushbullet/ -""" +"""Pushbullet platform for sensor component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/pushetta/notify.py b/homeassistant/components/pushetta/notify.py index 106c0641a699a2..028b0cfd49212a 100644 --- a/homeassistant/components/pushetta/notify.py +++ b/homeassistant/components/pushetta/notify.py @@ -1,9 +1,4 @@ -""" -Pushetta platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.pushetta/ -""" +"""Pushetta platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/pushover/notify.py b/homeassistant/components/pushover/notify.py index 78e9ed11c956e2..39a1ce5d2f7fdb 100644 --- a/homeassistant/components/pushover/notify.py +++ b/homeassistant/components/pushover/notify.py @@ -1,9 +1,4 @@ -""" -Pushover platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.pushover/ -""" +"""Pushover platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/pushsafer/notify.py b/homeassistant/components/pushsafer/notify.py index a1fa2b7409c6e7..c64b861631a904 100644 --- a/homeassistant/components/pushsafer/notify.py +++ b/homeassistant/components/pushsafer/notify.py @@ -1,9 +1,4 @@ -""" -Pushsafer platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.pushsafer/ -""" +"""Pushsafer platform for notify component.""" import base64 import logging import mimetypes diff --git a/homeassistant/components/pvoutput/sensor.py b/homeassistant/components/pvoutput/sensor.py index dbcd38af3ccb8d..22368212442786 100644 --- a/homeassistant/components/pvoutput/sensor.py +++ b/homeassistant/components/pvoutput/sensor.py @@ -1,9 +1,4 @@ -""" -Support for getting collected information from PVOutput. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pvoutput/ -""" +"""Support for getting collected information from PVOutput.""" import logging from collections import namedtuple from datetime import timedelta diff --git a/homeassistant/components/pyload/sensor.py b/homeassistant/components/pyload/sensor.py index 78a191c16f43a7..7c7d1e7ae08875 100644 --- a/homeassistant/components/pyload/sensor.py +++ b/homeassistant/components/pyload/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring pyLoad. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.pyload/ -""" +"""Support for monitoring pyLoad.""" from datetime import timedelta import logging diff --git a/homeassistant/components/python_script/__init__.py b/homeassistant/components/python_script/__init__.py index d639b638033dc5..56a82b3912054f 100644 --- a/homeassistant/components/python_script/__init__.py +++ b/homeassistant/components/python_script/__init__.py @@ -1,9 +1,4 @@ -""" -Component to allow running Python scripts. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/python_script/ -""" +"""Component to allow running Python scripts.""" import datetime import glob import logging diff --git a/homeassistant/components/qbittorrent/sensor.py b/homeassistant/components/qbittorrent/sensor.py index 8718f3a9d7449a..7e91c0ab276ccd 100644 --- a/homeassistant/components/qbittorrent/sensor.py +++ b/homeassistant/components/qbittorrent/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring the qBittorrent API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.qbittorrent/ -""" +"""Support for monitoring the qBittorrent API.""" import logging import voluptuous as vol diff --git a/homeassistant/components/qnap/sensor.py b/homeassistant/components/qnap/sensor.py index a6a9c6e30d074a..e12f20c25b1903 100644 --- a/homeassistant/components/qnap/sensor.py +++ b/homeassistant/components/qnap/sensor.py @@ -1,9 +1,4 @@ -""" -Support for QNAP NAS Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.qnap/ -""" +"""Support for QNAP NAS Sensors.""" import logging from datetime import timedelta diff --git a/homeassistant/components/qrcode/image_processing.py b/homeassistant/components/qrcode/image_processing.py index 00f4ad025b2071..46fa78cca7f94f 100644 --- a/homeassistant/components/qrcode/image_processing.py +++ b/homeassistant/components/qrcode/image_processing.py @@ -1,9 +1,4 @@ -""" -Support for the QR image processing. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.qr/ -""" +"""Support for the QR image processing.""" from homeassistant.core import split_entity_id from homeassistant.components.image_processing import ( ImageProcessingEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) diff --git a/homeassistant/components/quantum_gateway/device_tracker.py b/homeassistant/components/quantum_gateway/device_tracker.py index 90ba3575cfa966..3472a4dbb97677 100644 --- a/homeassistant/components/quantum_gateway/device_tracker.py +++ b/homeassistant/components/quantum_gateway/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Verizon FiOS Quantum Gateways. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.quantum_gateway/ -""" +"""Support for Verizon FiOS Quantum Gateways.""" import logging from requests.exceptions import RequestException diff --git a/homeassistant/components/qwikswitch/__init__.py b/homeassistant/components/qwikswitch/__init__.py index 23144ed82b8304..a64685956221fa 100644 --- a/homeassistant/components/qwikswitch/__init__.py +++ b/homeassistant/components/qwikswitch/__init__.py @@ -1,9 +1,4 @@ -""" -Support for Qwikswitch devices. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/qwikswitch/ -""" +"""Support for Qwikswitch devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/qwikswitch/binary_sensor.py b/homeassistant/components/qwikswitch/binary_sensor.py index 6cdc29deae4d66..a92c4d0b435e19 100644 --- a/homeassistant/components/qwikswitch/binary_sensor.py +++ b/homeassistant/components/qwikswitch/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Qwikswitch Binary Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.qwikswitch/ -""" +"""Support for Qwikswitch Binary Sensors.""" import logging from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/qwikswitch/light.py b/homeassistant/components/qwikswitch/light.py index 46a0a88483b2d5..cb4df24f9781df 100644 --- a/homeassistant/components/qwikswitch/light.py +++ b/homeassistant/components/qwikswitch/light.py @@ -1,9 +1,4 @@ -""" -Support for Qwikswitch Relays and Dimmers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.qwikswitch/ -""" +"""Support for Qwikswitch Relays and Dimmers.""" from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light from . import DOMAIN as QWIKSWITCH, QSToggleEntity diff --git a/homeassistant/components/qwikswitch/sensor.py b/homeassistant/components/qwikswitch/sensor.py index b9ccb3c3a7b524..8befce4f7e2a3f 100644 --- a/homeassistant/components/qwikswitch/sensor.py +++ b/homeassistant/components/qwikswitch/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Qwikswitch Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.qwikswitch/ -""" +"""Support for Qwikswitch Sensors.""" import logging from homeassistant.core import callback diff --git a/homeassistant/components/qwikswitch/switch.py b/homeassistant/components/qwikswitch/switch.py index ec544df8c7531d..4ee5396ae0ce3c 100644 --- a/homeassistant/components/qwikswitch/switch.py +++ b/homeassistant/components/qwikswitch/switch.py @@ -1,9 +1,4 @@ -""" -Support for Qwikswitch relays. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.qwikswitch/ -""" +"""Support for Qwikswitch relays.""" from homeassistant.components.switch import SwitchDevice from . import DOMAIN as QWIKSWITCH, QSToggleEntity diff --git a/homeassistant/components/rachio/__init__.py b/homeassistant/components/rachio/__init__.py index 27827da01827c4..64a7a5af4d7412 100644 --- a/homeassistant/components/rachio/__init__.py +++ b/homeassistant/components/rachio/__init__.py @@ -1,9 +1,4 @@ -""" -Integration with the Rachio Iro sprinkler system controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/rachio/ -""" +"""Integration with the Rachio Iro sprinkler system controller.""" import asyncio import logging from typing import Optional diff --git a/homeassistant/components/rachio/binary_sensor.py b/homeassistant/components/rachio/binary_sensor.py index 9cf57ea323064f..ffcaeccacff455 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Integration with the Rachio Iro sprinkler system controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.rachio/ -""" +"""Integration with the Rachio Iro sprinkler system controller.""" from abc import abstractmethod import logging diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index fe584441afd0fa..483e07e96f4ef3 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -1,9 +1,4 @@ -""" -Integration with the Rachio Iro sprinkler system controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.rachio/ -""" +"""Integration with the Rachio Iro sprinkler system controller.""" from abc import abstractmethod from datetime import timedelta import logging diff --git a/homeassistant/components/radarr/sensor.py b/homeassistant/components/radarr/sensor.py index 67695ae0e32b15..a3932acf862106 100644 --- a/homeassistant/components/radarr/sensor.py +++ b/homeassistant/components/radarr/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Radarr. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.radarr/ -""" +"""Support for Radarr.""" import logging import time from datetime import datetime, timedelta diff --git a/homeassistant/components/radiotherm/climate.py b/homeassistant/components/radiotherm/climate.py index 4132d3c27c743f..66dfc4cc385529 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -1,9 +1,4 @@ -""" -Support for Radio Thermostat wifi-enabled home thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.radiotherm/ -""" +"""Support for Radio Thermostat wifi-enabled home thermostats.""" import datetime import logging diff --git a/homeassistant/components/rainbird/__init__.py b/homeassistant/components/rainbird/__init__.py index bbce7f752af970..de0f42fda4a6b8 100644 --- a/homeassistant/components/rainbird/__init__.py +++ b/homeassistant/components/rainbird/__init__.py @@ -1,9 +1,4 @@ -""" -Support for Rain Bird Irrigation system LNK WiFi Module. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/rainbird/ -""" +"""Support for Rain Bird Irrigation system LNK WiFi Module.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rainbird/sensor.py b/homeassistant/components/rainbird/sensor.py index 3d0de04e53ef99..0cee202ecb20c8 100644 --- a/homeassistant/components/rainbird/sensor.py +++ b/homeassistant/components/rainbird/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Rain Bird Irrigation system LNK WiFi Module. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.rainbird/ -""" +"""Support for Rain Bird Irrigation system LNK WiFi Module.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rainbird/switch.py b/homeassistant/components/rainbird/switch.py index 2031769b343244..32c7c49ab99b7d 100644 --- a/homeassistant/components/rainbird/switch.py +++ b/homeassistant/components/rainbird/switch.py @@ -1,9 +1,4 @@ -""" -Support for Rain Bird Irrigation system LNK WiFi Module. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/switch.rainbird/ -""" +"""Support for Rain Bird Irrigation system LNK WiFi Module.""" import logging diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py index 7ccf9f33adacdf..473d52bdbdd119 100644 --- a/homeassistant/components/raincloud/__init__.py +++ b/homeassistant/components/raincloud/__init__.py @@ -1,9 +1,4 @@ -""" -Support for Melnor RainCloud sprinkler water timer. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/raincloud/ -""" +"""Support for Melnor RainCloud sprinkler water timer.""" from datetime import timedelta import logging diff --git a/homeassistant/components/raincloud/binary_sensor.py b/homeassistant/components/raincloud/binary_sensor.py index cb66fc3c6af8ec..6ebad7cc121819 100644 --- a/homeassistant/components/raincloud/binary_sensor.py +++ b/homeassistant/components/raincloud/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Melnor RainCloud sprinkler water timer. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.raincloud/ -""" +"""Support for Melnor RainCloud sprinkler water timer.""" import logging import voluptuous as vol diff --git a/homeassistant/components/raincloud/sensor.py b/homeassistant/components/raincloud/sensor.py index 8bcccf06171a28..6774d48ae99860 100644 --- a/homeassistant/components/raincloud/sensor.py +++ b/homeassistant/components/raincloud/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Melnor RainCloud sprinkler water timer. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.raincloud/ -""" +"""Support for Melnor RainCloud sprinkler water timer.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index 929dbcf314c403..4387e6b67bec05 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -1,9 +1,4 @@ -""" -This platform provides binary sensors for key RainMachine data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.rainmachine/ -""" +"""This platform provides binary sensors for key RainMachine data.""" import logging from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 908daa2c83dc0a..1d438b8035f8e2 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -1,9 +1,4 @@ -""" -This platform provides support for sensor data from RainMachine. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rainmachine/ -""" +"""This platform provides support for sensor data from RainMachine.""" import logging from homeassistant.core import callback diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index 6b658c0fcbf173..adcbe5598199f0 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -1,9 +1,4 @@ -""" -This component provides support for RainMachine programs and zones. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/switch.rainmachine/ -""" +"""This component provides support for RainMachine programs and zones.""" from datetime import datetime import logging diff --git a/homeassistant/components/random/binary_sensor.py b/homeassistant/components/random/binary_sensor.py index 9bdc57c6e468d3..ad8bafaf4c261c 100644 --- a/homeassistant/components/random/binary_sensor.py +++ b/homeassistant/components/random/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for showing random states. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.random/ -""" +"""Support for showing random states.""" import logging import voluptuous as vol diff --git a/homeassistant/components/random/sensor.py b/homeassistant/components/random/sensor.py index 4dec96bec2ef4a..cc412ff7773b07 100644 --- a/homeassistant/components/random/sensor.py +++ b/homeassistant/components/random/sensor.py @@ -1,9 +1,4 @@ -""" -Support for showing random numbers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.random/ -""" +"""Support for showing random numbers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/raspyrfm/switch.py b/homeassistant/components/raspyrfm/switch.py index a8a6230fc09fc9..a141721f3e5299 100644 --- a/homeassistant/components/raspyrfm/switch.py +++ b/homeassistant/components/raspyrfm/switch.py @@ -1,9 +1,4 @@ -""" -Support for switch devices that can be controlled using the RaspyRFM rc module. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.raspyrfm/ -""" +"""Support for switchs that can be controlled using the RaspyRFM rc module.""" import logging import voluptuous as vol diff --git a/homeassistant/components/recollect_waste/sensor.py b/homeassistant/components/recollect_waste/sensor.py index 9122973c919cf4..1e3803ab866020 100644 --- a/homeassistant/components/recollect_waste/sensor.py +++ b/homeassistant/components/recollect_waste/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Recollect Waste curbside collection pickup. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.recollect_waste/ -""" +"""Support for Recollect Waste curbside collection pickup.""" import logging import voluptuous as vol diff --git a/homeassistant/components/recswitch/switch.py b/homeassistant/components/recswitch/switch.py index 636c302cea118d..ed2da8022f8a1e 100644 --- a/homeassistant/components/recswitch/switch.py +++ b/homeassistant/components/recswitch/switch.py @@ -1,9 +1,4 @@ -""" -Support for Ankuoo RecSwitch MS6126 devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.recswitch/ -""" +"""Support for Ankuoo RecSwitch MS6126 devices.""" import logging diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index 1a94159290fd53..0d28e98229c7fe 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for RESTful binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.rest/ -""" +"""Support for RESTful binary sensors.""" import logging from requests.auth import HTTPBasicAuth, HTTPDigestAuth diff --git a/homeassistant/components/rest/notify.py b/homeassistant/components/rest/notify.py index de75db83848d90..8134e73ae6bdba 100644 --- a/homeassistant/components/rest/notify.py +++ b/homeassistant/components/rest/notify.py @@ -1,9 +1,4 @@ -""" -RESTful platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.rest/ -""" +"""RESTful platform for notify component.""" import logging import requests diff --git a/homeassistant/components/rest/sensor.py b/homeassistant/components/rest/sensor.py index a9446ee3503035..fe92f9d8a10059 100644 --- a/homeassistant/components/rest/sensor.py +++ b/homeassistant/components/rest/sensor.py @@ -1,9 +1,4 @@ -""" -Support for RESTful API sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rest/ -""" +"""Support for RESTful API sensors.""" import logging import json diff --git a/homeassistant/components/rest/switch.py b/homeassistant/components/rest/switch.py index 5f1920ae1af5f8..2ef45b226fe718 100644 --- a/homeassistant/components/rest/switch.py +++ b/homeassistant/components/rest/switch.py @@ -1,9 +1,4 @@ -""" -Support for RESTful switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.rest/ -""" +"""Support for RESTful switches.""" import asyncio import logging diff --git a/homeassistant/components/rflink/binary_sensor.py b/homeassistant/components/rflink/binary_sensor.py index 5318642a5b1688..e98fb756659cd7 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Rflink binary sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.rflink/ -""" +"""Support for Rflink binary sensors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rflink/cover.py b/homeassistant/components/rflink/cover.py index f91ef1cc6828bf..409d27862f9c3f 100644 --- a/homeassistant/components/rflink/cover.py +++ b/homeassistant/components/rflink/cover.py @@ -1,9 +1,4 @@ -""" -Support for Rflink Cover devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover.rflink/ -""" +"""Support for Rflink Cover devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rflink/light.py b/homeassistant/components/rflink/light.py index cdb34328b514a8..112ed4b4f51203 100644 --- a/homeassistant/components/rflink/light.py +++ b/homeassistant/components/rflink/light.py @@ -1,9 +1,4 @@ -""" -Support for Rflink lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.rflink/ -""" +"""Support for Rflink lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index e46cc09d0ba457..c7498ece2416d8 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Rflink sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rflink/ -""" +"""Support for Rflink sensors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/rflink/switch.py b/homeassistant/components/rflink/switch.py index a1470bf115fe8d..d5889c797f0e59 100644 --- a/homeassistant/components/rflink/switch.py +++ b/homeassistant/components/rflink/switch.py @@ -1,9 +1,4 @@ -""" -Support for Rflink switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.rflink/ -""" +"""Support for Rflink switches.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ring/binary_sensor.py b/homeassistant/components/ring/binary_sensor.py index bcc365a2e83a09..79de0424d85a85 100644 --- a/homeassistant/components/ring/binary_sensor.py +++ b/homeassistant/components/ring/binary_sensor.py @@ -1,9 +1,4 @@ -""" -This component provides HA sensor support for Ring Door Bell/Chimes. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.ring/ -""" +"""This component provides HA sensor support for Ring Door Bell/Chimes.""" from datetime import timedelta import logging diff --git a/homeassistant/components/ring/camera.py b/homeassistant/components/ring/camera.py index 905cbd46158fff..18427b9b6f9788 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -1,9 +1,4 @@ -""" -This component provides support to the Ring Door Bell camera. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.ring/ -""" +"""This component provides support to the Ring Door Bell camera.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index 5e323d89ad895d..c9cb2f1159a67f 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -1,9 +1,4 @@ -""" -This component provides HA sensor support for Ring Door Bell/Chimes. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.ring/ -""" +"""This component provides HA sensor support for Ring Door Bell/Chimes.""" from datetime import timedelta import logging diff --git a/homeassistant/components/ritassist/device_tracker.py b/homeassistant/components/ritassist/device_tracker.py index c41ae9f2ec2880..74bec1b871121f 100644 --- a/homeassistant/components/ritassist/device_tracker.py +++ b/homeassistant/components/ritassist/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for RitAssist Platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ritassist/ -""" +"""Support for RitAssist Platform.""" import logging import requests diff --git a/homeassistant/components/rmvtransport/sensor.py b/homeassistant/components/rmvtransport/sensor.py index 7835b74ac980fd..7a3afb3f324bca 100644 --- a/homeassistant/components/rmvtransport/sensor.py +++ b/homeassistant/components/rmvtransport/sensor.py @@ -1,9 +1,4 @@ -""" -Support for real-time departure information for Rhein-Main public transport. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rmvtransport/ -""" +"""Support for departure information for Rhein-Main public transport.""" import asyncio import logging from datetime import timedelta diff --git a/homeassistant/components/rocketchat/notify.py b/homeassistant/components/rocketchat/notify.py index 8bf1e172264aa4..e404114736a103 100644 --- a/homeassistant/components/rocketchat/notify.py +++ b/homeassistant/components/rocketchat/notify.py @@ -1,9 +1,4 @@ -""" -Rocket.Chat notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.rocketchat/ -""" +"""Rocket.Chat notification service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/roomba/vacuum.py b/homeassistant/components/roomba/vacuum.py index d06ecc5141f4b5..fadbe2a82d5254 100644 --- a/homeassistant/components/roomba/vacuum.py +++ b/homeassistant/components/roomba/vacuum.py @@ -1,9 +1,4 @@ -""" -Support for Wi-Fi enabled iRobot Roombas. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/vacuum.roomba/ -""" +"""Support for Wi-Fi enabled iRobot Roombas.""" import asyncio import logging diff --git a/homeassistant/components/rova/sensor.py b/homeassistant/components/rova/sensor.py index 07be331f23f35b..2c2c36b12457ad 100644 --- a/homeassistant/components/rova/sensor.py +++ b/homeassistant/components/rova/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Rova garbage calendar. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.rova/ -""" +"""Support for Rova garbage calendar.""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/rpi_camera/camera.py b/homeassistant/components/rpi_camera/camera.py index ba6f5e93304b5f..f0dd1d36539e4b 100644 --- a/homeassistant/components/rpi_camera/camera.py +++ b/homeassistant/components/rpi_camera/camera.py @@ -1,9 +1,4 @@ -""" -Camera platform that has a Raspberry Pi camera. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.rpi_camera/ -""" +"""Camera platform that has a Raspberry Pi camera.""" import os import subprocess import logging diff --git a/homeassistant/components/rpi_rf/switch.py b/homeassistant/components/rpi_rf/switch.py index 6844cb0f3836e3..d0a2337280296d 100644 --- a/homeassistant/components/rpi_rf/switch.py +++ b/homeassistant/components/rpi_rf/switch.py @@ -1,9 +1,4 @@ -""" -Allows to configure a switch using a 433MHz module via GPIO on a Raspberry Pi. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.rpi_rf/ -""" +"""Support for a switch using a 433MHz module via GPIO on a Raspberry Pi.""" import importlib import logging diff --git a/homeassistant/components/russound_rio/media_player.py b/homeassistant/components/russound_rio/media_player.py index 972594e07e606f..b8f9d29f5cae81 100644 --- a/homeassistant/components/russound_rio/media_player.py +++ b/homeassistant/components/russound_rio/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Russound multizone controllers using RIO Protocol. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.russound_rio/ -""" +"""Support for Russound multizone controllers using RIO Protocol.""" import logging import voluptuous as vol diff --git a/homeassistant/components/russound_rnet/media_player.py b/homeassistant/components/russound_rnet/media_player.py index 6d919cdf7a8a9e..f489d48a9d53f4 100644 --- a/homeassistant/components/russound_rnet/media_player.py +++ b/homeassistant/components/russound_rnet/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing with Russound via RNET Protocol. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.russound_rnet/ -""" +"""Support for interfacing with Russound via RNET Protocol.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ruter/sensor.py b/homeassistant/components/ruter/sensor.py index 91966f0df9c812..f6fefc96198939 100644 --- a/homeassistant/components/ruter/sensor.py +++ b/homeassistant/components/ruter/sensor.py @@ -1,9 +1,4 @@ -""" -A sensor platform that give you information about next departures from Ruter. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.ruter/ -""" +"""A sensor to provide information about next departures from Ruter.""" import logging import voluptuous as vol diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index e6715669da7812..1a2a24c3621040 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with an Samsung TV. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.samsungtv/ -""" +"""Support for interface with an Samsung TV.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index a6d16852df323d..e576eca78e872e 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -1,9 +1,4 @@ -""" -Support for getting data from websites with scraping. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.scrape/ -""" +"""Support for getting data from websites with scraping.""" import logging import voluptuous as vol diff --git a/homeassistant/components/scsgate/__init__.py b/homeassistant/components/scsgate/__init__.py index 79bf4e217c9e19..67421e9a46ad44 100644 --- a/homeassistant/components/scsgate/__init__.py +++ b/homeassistant/components/scsgate/__init__.py @@ -1,9 +1,4 @@ -""" -Support for SCSGate components. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/scsgate/ -""" +"""Support for SCSGate components.""" import logging from threading import Lock diff --git a/homeassistant/components/season/sensor.py b/homeassistant/components/season/sensor.py index 84a2b426e9e4b1..7c7b1054961bf3 100644 --- a/homeassistant/components/season/sensor.py +++ b/homeassistant/components/season/sensor.py @@ -1,9 +1,4 @@ -""" -Support for tracking which astronomical or meteorological season it is. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor/season/ -""" +"""Support for tracking which astronomical or meteorological season it is.""" import logging from datetime import datetime diff --git a/homeassistant/components/sendgrid/notify.py b/homeassistant/components/sendgrid/notify.py index 211e288725e9ab..a717c7f24edb0d 100644 --- a/homeassistant/components/sendgrid/notify.py +++ b/homeassistant/components/sendgrid/notify.py @@ -1,9 +1,4 @@ -""" -SendGrid notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.sendgrid/ -""" +"""SendGrid notification service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/sensehat/light.py b/homeassistant/components/sensehat/light.py index 86153fffef8cd9..c68e77b40a4d8d 100644 --- a/homeassistant/components/sensehat/light.py +++ b/homeassistant/components/sensehat/light.py @@ -1,9 +1,4 @@ -""" -Support for Sense Hat LEDs. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.sensehat/ -""" +"""Support for Sense Hat LEDs.""" import logging import voluptuous as vol diff --git a/homeassistant/components/sensehat/sensor.py b/homeassistant/components/sensehat/sensor.py index 15c73d990e17d1..870150c1a987cd 100644 --- a/homeassistant/components/sensehat/sensor.py +++ b/homeassistant/components/sensehat/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Sense HAT sensors. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.sensehat -""" +"""Support for Sense HAT sensors.""" import os import logging from datetime import timedelta diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 3affaba3e1f83f..bf06f232427a3f 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -1,9 +1,4 @@ -""" -Support for Sensibo wifi-enabled home thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.sensibo/ -""" +"""Support for Sensibo wifi-enabled home thermostats.""" import asyncio import logging diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 50549f28fd7be7..031657066cbdd5 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with various sensors that can be monitored. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor/ -""" +"""Component to interface with various sensors that can be monitored.""" from datetime import timedelta import logging diff --git a/homeassistant/components/serial/sensor.py b/homeassistant/components/serial/sensor.py index 5d49b065558b25..c01981f90218a7 100644 --- a/homeassistant/components/serial/sensor.py +++ b/homeassistant/components/serial/sensor.py @@ -1,9 +1,4 @@ -""" -Support for reading data from a serial port. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.serial/ -""" +"""Support for reading data from a serial port.""" import logging import json diff --git a/homeassistant/components/serial_pm/sensor.py b/homeassistant/components/serial_pm/sensor.py index 46dfc9fae75279..9ad65f7256fa25 100644 --- a/homeassistant/components/serial_pm/sensor.py +++ b/homeassistant/components/serial_pm/sensor.py @@ -1,9 +1,4 @@ -""" -Support for particulate matter sensors connected to a serial port. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.serial_pm/ -""" +"""Support for particulate matter sensors connected to a serial port.""" import logging import voluptuous as vol diff --git a/homeassistant/components/sesame/lock.py b/homeassistant/components/sesame/lock.py index 44a6cfb265c1f9..263914f389cced 100644 --- a/homeassistant/components/sesame/lock.py +++ b/homeassistant/components/sesame/lock.py @@ -1,9 +1,4 @@ -""" -Support for Sesame, by CANDY HOUSE. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/lock.sesame/ -""" +"""Support for Sesame, by CANDY HOUSE.""" from typing import Callable import voluptuous as vol diff --git a/homeassistant/components/seven_segments/image_processing.py b/homeassistant/components/seven_segments/image_processing.py index a460115cc34db1..7bbfceb15e4d17 100644 --- a/homeassistant/components/seven_segments/image_processing.py +++ b/homeassistant/components/seven_segments/image_processing.py @@ -1,9 +1,4 @@ -""" -Local optical character recognition processing of seven segments displays. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/image_processing.seven_segments/ -""" +"""Optical character recognition processing of seven segments displays.""" import logging import io import os diff --git a/homeassistant/components/seventeentrack/sensor.py b/homeassistant/components/seventeentrack/sensor.py index 6fb4884989b7e1..ff17d1a4c546fc 100644 --- a/homeassistant/components/seventeentrack/sensor.py +++ b/homeassistant/components/seventeentrack/sensor.py @@ -1,9 +1,4 @@ -""" -Support for package tracking sensors from 17track.net. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.seventeentrack/ -""" +"""Support for package tracking sensors from 17track.net.""" import logging from datetime import timedelta diff --git a/homeassistant/components/sht31/sensor.py b/homeassistant/components/sht31/sensor.py index 4b8498497715d6..613b1f8c92a16b 100644 --- a/homeassistant/components/sht31/sensor.py +++ b/homeassistant/components/sht31/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Sensirion SHT31 temperature and humidity sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sht31/ -""" +"""Support for Sensirion SHT31 temperature and humidity sensor.""" from datetime import timedelta import logging diff --git a/homeassistant/components/sigfox/sensor.py b/homeassistant/components/sigfox/sensor.py index 5e2a56cadc36ee..1bce2d6b28daff 100644 --- a/homeassistant/components/sigfox/sensor.py +++ b/homeassistant/components/sigfox/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for SigFox devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sigfox/ -""" +"""Sensor for SigFox devices.""" import logging import datetime import json diff --git a/homeassistant/components/simplepush/notify.py b/homeassistant/components/simplepush/notify.py index 63222d4adc1f52..081351238d9d0e 100644 --- a/homeassistant/components/simplepush/notify.py +++ b/homeassistant/components/simplepush/notify.py @@ -1,9 +1,4 @@ -""" -Simplepush notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.simplepush/ -""" +"""Simplepush notification service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/simulated/sensor.py b/homeassistant/components/simulated/sensor.py index 8f2c3dd36e042a..562f355f76b911 100644 --- a/homeassistant/components/simulated/sensor.py +++ b/homeassistant/components/simulated/sensor.py @@ -1,9 +1,4 @@ -""" -Adds a simulated sensor. - -For more details about this platform, refer to the documentation at -https://home-assistant.io/components/sensor.simulated/ -""" +"""Adds a simulated sensor.""" import logging import math from random import Random diff --git a/homeassistant/components/sky_hub/device_tracker.py b/homeassistant/components/sky_hub/device_tracker.py index 0d69e08aa7132d..4e0ce4352cc769 100644 --- a/homeassistant/components/sky_hub/device_tracker.py +++ b/homeassistant/components/sky_hub/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Sky Hub. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.sky_hub/ -""" +"""Support for Sky Hub.""" import logging import re diff --git a/homeassistant/components/skybeacon/sensor.py b/homeassistant/components/skybeacon/sensor.py index 6960999306dbe5..9b8b4872cdce89 100644 --- a/homeassistant/components/skybeacon/sensor.py +++ b/homeassistant/components/skybeacon/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Skybeacon temperature/humidity Bluetooth LE sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.skybeacon/ -""" +"""Support for Skybeacon temperature/humidity Bluetooth LE sensors.""" import logging import threading from uuid import UUID diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index eabddf012993db..026fed0a58ebb2 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -1,9 +1,4 @@ -""" -Slack platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.slack/ -""" +"""Slack platform for notify component.""" import logging import requests diff --git a/homeassistant/components/sleepiq/binary_sensor.py b/homeassistant/components/sleepiq/binary_sensor.py index 808eda4967db28..11f9e25d8c957e 100644 --- a/homeassistant/components/sleepiq/binary_sensor.py +++ b/homeassistant/components/sleepiq/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for SleepIQ sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.sleepiq/ -""" +"""Support for SleepIQ sensors.""" from homeassistant.components import sleepiq from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/sleepiq/sensor.py b/homeassistant/components/sleepiq/sensor.py index 2c97d7eb1e12f7..3de444c332452d 100644 --- a/homeassistant/components/sleepiq/sensor.py +++ b/homeassistant/components/sleepiq/sensor.py @@ -1,9 +1,4 @@ -""" -Support for SleepIQ sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sleepiq/ -""" +"""Support for SleepIQ sensors.""" from homeassistant.components import sleepiq DEPENDENCIES = ['sleepiq'] diff --git a/homeassistant/components/sma/sensor.py b/homeassistant/components/sma/sensor.py index 61009a472fb028..a2ec7871f608e0 100644 --- a/homeassistant/components/sma/sensor.py +++ b/homeassistant/components/sma/sensor.py @@ -1,9 +1,4 @@ -""" -SMA Solar Webconnect interface. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sma/ -""" +"""SMA Solar Webconnect interface.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index 548a38711bd176..9aa44d26f2dd7e 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -1,10 +1,4 @@ -""" -SmartApp functionality to receive cloud-push notifications. - -This module defines the functions to manage the SmartApp integration -within the SmartThings ecosystem in order to receive real-time webhook-based -callbacks when device states change. -""" +"""SmartApp functionality to receive cloud-push notifications.""" import asyncio import functools import logging diff --git a/homeassistant/components/smtp/notify.py b/homeassistant/components/smtp/notify.py index 4104013bcf7236..1aaf3464e2b3d8 100644 --- a/homeassistant/components/smtp/notify.py +++ b/homeassistant/components/smtp/notify.py @@ -1,9 +1,4 @@ -""" -Mail (SMTP) notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.smtp/ -""" +"""Mail (SMTP) notification service.""" from email.mime.application import MIMEApplication from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart diff --git a/homeassistant/components/snapcast/media_player.py b/homeassistant/components/snapcast/media_player.py index 74b17ae5ff1a6e..b1589c4db51ab3 100644 --- a/homeassistant/components/snapcast/media_player.py +++ b/homeassistant/components/snapcast/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interacting with Snapcast clients. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.snapcast/ -""" +"""Support for interacting with Snapcast clients.""" import logging import socket diff --git a/homeassistant/components/snmp/device_tracker.py b/homeassistant/components/snmp/device_tracker.py index 7c6efc82ef9d74..8a0fe7c6101c98 100644 --- a/homeassistant/components/snmp/device_tracker.py +++ b/homeassistant/components/snmp/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for fetching WiFi associations through SNMP. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.snmp/ -""" +"""Support for fetching WiFi associations through SNMP.""" import binascii import logging diff --git a/homeassistant/components/snmp/sensor.py b/homeassistant/components/snmp/sensor.py index 3964e44e376860..83d311189885ef 100644 --- a/homeassistant/components/snmp/sensor.py +++ b/homeassistant/components/snmp/sensor.py @@ -1,9 +1,4 @@ -""" -Support for displaying collected data over SNMP. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.snmp/ -""" +"""Support for displaying collected data over SNMP.""" import logging from datetime import timedelta diff --git a/homeassistant/components/snmp/switch.py b/homeassistant/components/snmp/switch.py index 0baa129657dadc..fdb3267a3c7c32 100644 --- a/homeassistant/components/snmp/switch.py +++ b/homeassistant/components/snmp/switch.py @@ -1,9 +1,4 @@ -""" -Support for SNMP enabled switch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.snmp/ -""" +"""Support for SNMP enabled switch.""" import logging import voluptuous as vol diff --git a/homeassistant/components/socialblade/sensor.py b/homeassistant/components/socialblade/sensor.py index 9a73e9cdd68230..77433ac6d57d4a 100644 --- a/homeassistant/components/socialblade/sensor.py +++ b/homeassistant/components/socialblade/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Social Blade. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.socialblade/ -""" +"""Support for Social Blade.""" from datetime import timedelta import logging diff --git a/homeassistant/components/solaredge/sensor.py b/homeassistant/components/solaredge/sensor.py index d56ccc53b68672..6c6d7557282e2f 100644 --- a/homeassistant/components/solaredge/sensor.py +++ b/homeassistant/components/solaredge/sensor.py @@ -1,9 +1,4 @@ -""" -Support for SolarEdge Monitoring API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.solaredge/ -""" +"""Support for SolarEdge Monitoring API.""" from datetime import timedelta import logging diff --git a/homeassistant/components/sonarr/sensor.py b/homeassistant/components/sonarr/sensor.py index b0e87992e39d66..b593f6d3182f1b 100644 --- a/homeassistant/components/sonarr/sensor.py +++ b/homeassistant/components/sonarr/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Sonarr. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sonarr/ -""" +"""Support for Sonarr.""" import logging import time from datetime import datetime diff --git a/homeassistant/components/songpal/media_player.py b/homeassistant/components/songpal/media_player.py index 7665b409d1d237..842360484cfd5b 100644 --- a/homeassistant/components/songpal/media_player.py +++ b/homeassistant/components/songpal/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Songpal-enabled (Sony) media devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.songpal/ -""" +"""Support for Songpal-enabled (Sony) media devices.""" import asyncio import logging from collections import OrderedDict diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py index b2045b9b65eb7b..027fad43a4013b 100644 --- a/homeassistant/components/soundtouch/media_player.py +++ b/homeassistant/components/soundtouch/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with a Bose Soundtouch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.soundtouch/ -""" +"""Support for interface with a Bose Soundtouch.""" import logging import re diff --git a/homeassistant/components/spc/alarm_control_panel.py b/homeassistant/components/spc/alarm_control_panel.py index 623a4b0dbd1d88..77b412021aa142 100644 --- a/homeassistant/components/spc/alarm_control_panel.py +++ b/homeassistant/components/spc/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Support for Vanderbilt (formerly Siemens) SPC alarm systems. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.spc/ -""" +"""Support for Vanderbilt (formerly Siemens) SPC alarm systems.""" import logging import homeassistant.components.alarm_control_panel as alarm diff --git a/homeassistant/components/spc/binary_sensor.py b/homeassistant/components/spc/binary_sensor.py index 6a0712d62bb58a..78ec2a11a97c1a 100644 --- a/homeassistant/components/spc/binary_sensor.py +++ b/homeassistant/components/spc/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Vanderbilt (formerly Siemens) SPC alarm systems. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.spc/ -""" +"""Support for Vanderbilt (formerly Siemens) SPC alarm systems.""" import logging from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/spotcrime/sensor.py b/homeassistant/components/spotcrime/sensor.py index 46f5fdc1c85c97..fa9cfa687ec3af 100644 --- a/homeassistant/components/spotcrime/sensor.py +++ b/homeassistant/components/spotcrime/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for Spot Crime. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.spotcrime/ -""" +"""Sensor for Spot Crime.""" from datetime import timedelta from collections import defaultdict diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index 9965487ded9b7e..b9252d5035bd9f 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interacting with Spotify Connect. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.spotify/ -""" +"""Support for interacting with Spotify Connect.""" from datetime import timedelta import logging diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index 5f6fd525a112e7..d25d2f03fce720 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interfacing to the Logitech SqueezeBox API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.squeezebox/ -""" +"""Support for interfacing to the Logitech SqueezeBox API.""" import asyncio import json import logging diff --git a/homeassistant/components/srp_energy/sensor.py b/homeassistant/components/srp_energy/sensor.py index 4d2cd863b12f7f..0ebae427da138a 100644 --- a/homeassistant/components/srp_energy/sensor.py +++ b/homeassistant/components/srp_energy/sensor.py @@ -1,9 +1,4 @@ -""" -Platform for retrieving energy data from SRP. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/sensor.srp_energy/ -""" +"""Platform for retrieving energy data from SRP.""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/starlingbank/sensor.py b/homeassistant/components/starlingbank/sensor.py index e325e5e1a57cde..00640ea49632ca 100644 --- a/homeassistant/components/starlingbank/sensor.py +++ b/homeassistant/components/starlingbank/sensor.py @@ -1,9 +1,4 @@ -""" -Support for balance data via the Starling Bank API. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.starlingbank/ -""" +"""Support for balance data via the Starling Bank API.""" import logging import requests diff --git a/homeassistant/components/startca/sensor.py b/homeassistant/components/startca/sensor.py index 85939ea72ae571..1e57a4cf859eba 100644 --- a/homeassistant/components/startca/sensor.py +++ b/homeassistant/components/startca/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Start.ca Bandwidth Monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.startca/ -""" +"""Support for Start.ca Bandwidth Monitor.""" from datetime import timedelta from xml.parsers.expat import ExpatError import logging diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index 01c783dc1db25b..a777a921f314e4 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -1,9 +1,4 @@ -""" -Support for statistics for sensor values. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.statistics/ -""" +"""Support for statistics for sensor values.""" import logging import statistics from collections import deque diff --git a/homeassistant/components/steam_online/sensor.py b/homeassistant/components/steam_online/sensor.py index 861a5958dd30d8..4b4b73ad8cfd2f 100644 --- a/homeassistant/components/steam_online/sensor.py +++ b/homeassistant/components/steam_online/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for Steam account status. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.steam_online/ -""" +"""Sensor for Steam account status.""" import logging import voluptuous as vol diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 1e8ae5d60e3be8..43debc504e1a15 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -1,9 +1,4 @@ -""" -Provide functionality to stream video source. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/stream/ -""" +"""Provide functionality to stream video source.""" import logging import threading diff --git a/homeassistant/components/stream/hls.py b/homeassistant/components/stream/hls.py index aa5ce1057643d6..c19db4f203f323 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -1,9 +1,4 @@ -""" -Provide functionality to stream HLS. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/stream/hls -""" +"""Provide functionality to stream HLS.""" from aiohttp import web from homeassistant.core import callback diff --git a/homeassistant/components/stride/notify.py b/homeassistant/components/stride/notify.py index 9d05bd17f34c51..fa08697d798579 100644 --- a/homeassistant/components/stride/notify.py +++ b/homeassistant/components/stride/notify.py @@ -1,9 +1,4 @@ -""" -Stride platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.stride/ -""" +"""Stride platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/supervisord/sensor.py b/homeassistant/components/supervisord/sensor.py index 894881dad86a34..fc40bd4e86774f 100644 --- a/homeassistant/components/supervisord/sensor.py +++ b/homeassistant/components/supervisord/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for Supervisord process status. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.supervisord/ -""" +"""Sensor for Supervisord process status.""" import logging import xmlrpc.client diff --git a/homeassistant/components/swiss_hydrological_data/sensor.py b/homeassistant/components/swiss_hydrological_data/sensor.py index c354ebedb2b68e..84964a94cbd141 100644 --- a/homeassistant/components/swiss_hydrological_data/sensor.py +++ b/homeassistant/components/swiss_hydrological_data/sensor.py @@ -1,9 +1,4 @@ -""" -Support for hydrological data from the Federal Office for the Environment FOEN. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.swiss_hydrological_data/ -""" +"""Support for hydrological data from the Fed. Office for the Environment.""" from datetime import timedelta import logging diff --git a/homeassistant/components/swiss_public_transport/sensor.py b/homeassistant/components/swiss_public_transport/sensor.py index d9f2410f8cafab..8d6b7fdee0ea85 100644 --- a/homeassistant/components/swiss_public_transport/sensor.py +++ b/homeassistant/components/swiss_public_transport/sensor.py @@ -1,9 +1,4 @@ -""" -Support for transport.opendata.ch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.swiss_public_transport/ -""" +"""Support for transport.opendata.ch.""" from datetime import timedelta import logging diff --git a/homeassistant/components/swisscom/device_tracker.py b/homeassistant/components/swisscom/device_tracker.py index d5826ecedff669..7371762da9264f 100644 --- a/homeassistant/components/swisscom/device_tracker.py +++ b/homeassistant/components/swisscom/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Swisscom routers (Internet-Box). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.swisscom/ -""" +"""Support for Swisscom routers (Internet-Box).""" import logging from aiohttp.hdrs import CONTENT_TYPE diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index d517f635a9241a..7e89a5369c8453 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -1,9 +1,4 @@ -""" -Component to interface with various switches that can be controlled remotely. - -For more details about this component, please refer to the documentation -at https://home-assistant.io/components/switch/ -""" +"""Component to interface with switches that can be controlled remotely.""" from datetime import timedelta import logging diff --git a/homeassistant/components/switch/light.py b/homeassistant/components/switch/light.py index 64f8779e4ab611..8f9e489bd9ca37 100644 --- a/homeassistant/components/switch/light.py +++ b/homeassistant/components/switch/light.py @@ -1,9 +1,4 @@ -""" -Light support for switch entities. - -For more information about this platform, please refer to the documentation at -https://home-assistant.io/components/light.switch/ -""" +"""Light support for switch entities.""" import logging import voluptuous as vol diff --git a/homeassistant/components/switchmate/switch.py b/homeassistant/components/switchmate/switch.py index 60497e0207b707..c14a6ca80879bd 100644 --- a/homeassistant/components/switchmate/switch.py +++ b/homeassistant/components/switchmate/switch.py @@ -1,9 +1,4 @@ -""" -Support for Switchmate. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.switchmate/ -""" +"""Support for Switchmate.""" import logging from datetime import timedelta diff --git a/homeassistant/components/syncthru/sensor.py b/homeassistant/components/syncthru/sensor.py index 862efb63fd7618..5596d4ab86adf9 100644 --- a/homeassistant/components/syncthru/sensor.py +++ b/homeassistant/components/syncthru/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Samsung Printers with SyncThru web interface. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.syncthru/ -""" +"""Support for Samsung Printers with SyncThru web interface.""" import logging import voluptuous as vol diff --git a/homeassistant/components/synology/camera.py b/homeassistant/components/synology/camera.py index b094cf98edfbdc..c452f60cc2a1d2 100644 --- a/homeassistant/components/synology/camera.py +++ b/homeassistant/components/synology/camera.py @@ -1,9 +1,4 @@ -""" -Support for Synology Surveillance Station Cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.synology/ -""" +"""Support for Synology Surveillance Station Cameras.""" import logging import requests diff --git a/homeassistant/components/synology_chat/notify.py b/homeassistant/components/synology_chat/notify.py index 32277dc1971864..8f2f654da3c0a8 100644 --- a/homeassistant/components/synology_chat/notify.py +++ b/homeassistant/components/synology_chat/notify.py @@ -1,9 +1,4 @@ -""" -SynologyChat platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.synology_chat/ -""" +"""SynologyChat platform for notify component.""" import json import logging diff --git a/homeassistant/components/syslog/notify.py b/homeassistant/components/syslog/notify.py index 740148e28e505c..2e6c3bf6123620 100644 --- a/homeassistant/components/syslog/notify.py +++ b/homeassistant/components/syslog/notify.py @@ -1,9 +1,4 @@ -""" -Syslog notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.syslog/ -""" +"""Syslog notification service.""" import logging import voluptuous as vol diff --git a/homeassistant/components/sytadin/sensor.py b/homeassistant/components/sytadin/sensor.py index f8ef18fcffe748..517deda7ca2b71 100644 --- a/homeassistant/components/sytadin/sensor.py +++ b/homeassistant/components/sytadin/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Sytadin Traffic, French Traffic Supervision. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.sytadin/ -""" +"""Support for Sytadin Traffic, French Traffic Supervision.""" import logging import re from datetime import timedelta diff --git a/homeassistant/components/tank_utility/sensor.py b/homeassistant/components/tank_utility/sensor.py index c807f1aa4c7c83..5389d60ef461db 100644 --- a/homeassistant/components/tank_utility/sensor.py +++ b/homeassistant/components/tank_utility/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Tank Utility propane monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.tank_utility/ -""" +"""Support for the Tank Utility propane monitor.""" import datetime import logging diff --git a/homeassistant/components/tapsaff/binary_sensor.py b/homeassistant/components/tapsaff/binary_sensor.py index 1978a127c17e16..639e9574ed971e 100644 --- a/homeassistant/components/tapsaff/binary_sensor.py +++ b/homeassistant/components/tapsaff/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for Taps Affs. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.tapsaff/ -""" +"""Support for Taps Affs.""" from datetime import timedelta import logging diff --git a/homeassistant/components/tautulli/sensor.py b/homeassistant/components/tautulli/sensor.py index 5c48731f7df03f..44be10749bfa36 100644 --- a/homeassistant/components/tautulli/sensor.py +++ b/homeassistant/components/tautulli/sensor.py @@ -1,9 +1,4 @@ -""" -A platform which allows you to get information from Tautulli. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/sensor.tautulli/ -""" +"""A platform which allows you to get information from Tautulli.""" from datetime import timedelta import logging diff --git a/homeassistant/components/tcp/binary_sensor.py b/homeassistant/components/tcp/binary_sensor.py index 80d77cd52a168f..4d26d819ede2df 100644 --- a/homeassistant/components/tcp/binary_sensor.py +++ b/homeassistant/components/tcp/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Provides a binary sensor which gets its values from a TCP socket. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.tcp/ -""" +"""Provides a binary sensor which gets its values from a TCP socket.""" import logging from homeassistant.components.binary_sensor import BinarySensorDevice diff --git a/homeassistant/components/tcp/sensor.py b/homeassistant/components/tcp/sensor.py index d214bd3d425fa8..6788848df07910 100644 --- a/homeassistant/components/tcp/sensor.py +++ b/homeassistant/components/tcp/sensor.py @@ -1,9 +1,4 @@ -""" -Support for TCP socket based sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.tcp/ -""" +"""Support for TCP socket based sensors.""" import logging import socket import select diff --git a/homeassistant/components/ted5000/sensor.py b/homeassistant/components/ted5000/sensor.py index 23a20b3e830fa9..fba9866302d984 100644 --- a/homeassistant/components/ted5000/sensor.py +++ b/homeassistant/components/ted5000/sensor.py @@ -1,9 +1,4 @@ -""" -Support gathering ted500 information. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.ted5000/ -""" +"""Support gathering ted500 information.""" import logging from datetime import timedelta diff --git a/homeassistant/components/teksavvy/sensor.py b/homeassistant/components/teksavvy/sensor.py index 0be18cbd6b6f28..de74ceda9f5be5 100644 --- a/homeassistant/components/teksavvy/sensor.py +++ b/homeassistant/components/teksavvy/sensor.py @@ -1,9 +1,4 @@ -""" -Support for TekSavvy Bandwidth Monitor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.teksavvy/ -""" +"""Support for TekSavvy Bandwidth Monitor.""" from datetime import timedelta import logging import async_timeout diff --git a/homeassistant/components/telegram/notify.py b/homeassistant/components/telegram/notify.py index 428c7e093d2365..3602bbd24419f7 100644 --- a/homeassistant/components/telegram/notify.py +++ b/homeassistant/components/telegram/notify.py @@ -1,9 +1,4 @@ -""" -Telegram platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.telegram/ -""" +"""Telegram platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/telnet/switch.py b/homeassistant/components/telnet/switch.py index 7c3baf2981a02a..6ad7e7b43a9f49 100644 --- a/homeassistant/components/telnet/switch.py +++ b/homeassistant/components/telnet/switch.py @@ -1,9 +1,4 @@ -""" -Support for switch controlled using a telnet connection. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.telnet/ -""" +"""Support for switch controlled using a telnet connection.""" from datetime import timedelta import logging import telnetlib diff --git a/homeassistant/components/temper/sensor.py b/homeassistant/components/temper/sensor.py index 72184df7c8f28f..1c6cb9fdff4636 100644 --- a/homeassistant/components/temper/sensor.py +++ b/homeassistant/components/temper/sensor.py @@ -1,9 +1,4 @@ -""" -Support for getting temperature from TEMPer devices. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.temper/ -""" +"""Support for getting temperature from TEMPer devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/binary_sensor.py b/homeassistant/components/template/binary_sensor.py index 605ab24a264170..bd9c4dfc698849 100644 --- a/homeassistant/components/template/binary_sensor.py +++ b/homeassistant/components/template/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for exposing a templated binary sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.template/ -""" +"""Support for exposing a templated binary sensor.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index 1d3642a6036164..2fdcc9f1036aa7 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -1,9 +1,4 @@ -""" -Support for covers which integrate with other components. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/cover.template/ -""" +"""Support for covers which integrate with other components.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index d9182b79a40a5f..cc6505d22f7304 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -1,9 +1,4 @@ -""" -Support for Template fans. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/fan.template/ -""" +"""Support for Template fans.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index bf930dd1b38bbc..980f0ad152c923 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -1,9 +1,4 @@ -""" -Support for Template lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.template/ -""" +"""Support for Template lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/lock.py b/homeassistant/components/template/lock.py index 527af4c5b85bbf..ad5d0c4aea75d8 100644 --- a/homeassistant/components/template/lock.py +++ b/homeassistant/components/template/lock.py @@ -1,9 +1,4 @@ -""" -Support for locks which integrates with other components. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/lock.template/ -""" +"""Support for locks which integrates with other components.""" import logging import voluptuous as vol diff --git a/homeassistant/components/template/sensor.py b/homeassistant/components/template/sensor.py index 5f3af4a06a44cc..41dc6f8aeebe1e 100644 --- a/homeassistant/components/template/sensor.py +++ b/homeassistant/components/template/sensor.py @@ -1,9 +1,4 @@ -""" -Allows the creation of a sensor that breaks out state_attributes. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.template/ -""" +"""Allows the creation of a sensor that breaks out state_attributes.""" import logging from typing import Optional diff --git a/homeassistant/components/template/switch.py b/homeassistant/components/template/switch.py index a2098c2f5fd58d..541bfd3bcfee6d 100644 --- a/homeassistant/components/template/switch.py +++ b/homeassistant/components/template/switch.py @@ -1,9 +1,4 @@ -""" -Support for switches which integrates with other components. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.template/ -""" +"""Support for switches which integrates with other components.""" import logging import voluptuous as vol diff --git a/homeassistant/components/thomson/device_tracker.py b/homeassistant/components/thomson/device_tracker.py index 8a56fcee7024b5..bbce443696dea1 100644 --- a/homeassistant/components/thomson/device_tracker.py +++ b/homeassistant/components/thomson/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for THOMSON routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.thomson/ -""" +"""Support for THOMSON routers.""" import logging import re import telnetlib diff --git a/homeassistant/components/threshold/binary_sensor.py b/homeassistant/components/threshold/binary_sensor.py index 0dadf3a61fdc63..916a5b968b2cae 100644 --- a/homeassistant/components/threshold/binary_sensor.py +++ b/homeassistant/components/threshold/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring if a sensor value is below/above a threshold. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.threshold/ -""" +"""Support for monitoring if a sensor value is below/above a threshold.""" import logging import voluptuous as vol diff --git a/homeassistant/components/tikteck/light.py b/homeassistant/components/tikteck/light.py index 64b4069d98e4b7..4f5596c71be4d7 100644 --- a/homeassistant/components/tikteck/light.py +++ b/homeassistant/components/tikteck/light.py @@ -1,9 +1,4 @@ -""" -Support for Tikteck lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.tikteck/ -""" +"""Support for Tikteck lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/tile/device_tracker.py b/homeassistant/components/tile/device_tracker.py index 6da520280e2e4b..c471c1e23b4d49 100644 --- a/homeassistant/components/tile/device_tracker.py +++ b/homeassistant/components/tile/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Tile® Bluetooth trackers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.tile/ -""" +"""Support for Tile® Bluetooth trackers.""" import logging from datetime import timedelta diff --git a/homeassistant/components/time_date/sensor.py b/homeassistant/components/time_date/sensor.py index 7825867df64fa4..5342dc576921ff 100644 --- a/homeassistant/components/time_date/sensor.py +++ b/homeassistant/components/time_date/sensor.py @@ -1,9 +1,4 @@ -""" -Support for showing the date and the time. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.time_date/ -""" +"""Support for showing the date and the time.""" from datetime import timedelta import logging diff --git a/homeassistant/components/todoist/calendar.py b/homeassistant/components/todoist/calendar.py index a0a3457667fbf5..313935e1221fe8 100644 --- a/homeassistant/components/todoist/calendar.py +++ b/homeassistant/components/todoist/calendar.py @@ -1,9 +1,4 @@ -""" -Support for Todoist task management (https://todoist.com). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/calendar.todoist/ -""" +"""Support for Todoist task management (https://todoist.com).""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/tomato/device_tracker.py b/homeassistant/components/tomato/device_tracker.py index 718adad421231b..9d0506fe042cd5 100644 --- a/homeassistant/components/tomato/device_tracker.py +++ b/homeassistant/components/tomato/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Tomato routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.tomato/ -""" +"""Support for Tomato routers.""" import json import logging import re diff --git a/homeassistant/components/torque/sensor.py b/homeassistant/components/torque/sensor.py index 4941633677c833..2f947c178b89ca 100644 --- a/homeassistant/components/torque/sensor.py +++ b/homeassistant/components/torque/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Torque OBD application. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.torque/ -""" +"""Support for the Torque OBD application.""" import logging import re diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index a272a22abe509d..c56c4ed95a63fe 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Interfaces with TotalConnect alarm control panels. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.totalconnect/ -""" +"""Interfaces with TotalConnect alarm control panels.""" import logging import voluptuous as vol diff --git a/homeassistant/components/touchline/climate.py b/homeassistant/components/touchline/climate.py index fa38bd37c8f7ec..e003ea257d75a9 100644 --- a/homeassistant/components/touchline/climate.py +++ b/homeassistant/components/touchline/climate.py @@ -1,9 +1,4 @@ -""" -Platform for Roth Touchline heat pump controller. - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/climate.touchline/ -""" +"""Platform for Roth Touchline heat pump controller.""" import logging import voluptuous as vol diff --git a/homeassistant/components/tplink/device_tracker.py b/homeassistant/components/tplink/device_tracker.py index 33a5d5f32f8310..7f5c4a37d2451c 100644 --- a/homeassistant/components/tplink/device_tracker.py +++ b/homeassistant/components/tplink/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for TP-Link routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.tplink/ -""" +"""Support for TP-Link routers.""" import base64 from datetime import datetime import hashlib diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index 0ba1dfaa33a2c3..9f13766c4eff9d 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -1,9 +1,4 @@ -""" -Support for TPLink lights. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/light.tplink/ -""" +"""Support for TPLink lights.""" import logging import time diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index a75945e99565e2..a4eeadd1c60a7e 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -1,9 +1,4 @@ -""" -Support for TPLink HS100/HS110/HS200 smart switch. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.tplink/ -""" +"""Support for TPLink HS100/HS110/HS200 smart switch.""" import logging import time diff --git a/homeassistant/components/traccar/device_tracker.py b/homeassistant/components/traccar/device_tracker.py index e3ac14279414c9..28d13dd4fe6fcc 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Traccar device tracking. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.traccar/ -""" +"""Support for Traccar device tracking.""" from datetime import datetime, timedelta import logging diff --git a/homeassistant/components/trackr/device_tracker.py b/homeassistant/components/trackr/device_tracker.py index 08d3a4c944588e..1322fde7e1ae33 100644 --- a/homeassistant/components/trackr/device_tracker.py +++ b/homeassistant/components/trackr/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for the TrackR platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.trackr/ -""" +"""Support for the TrackR platform.""" import logging import voluptuous as vol diff --git a/homeassistant/components/trafikverket_weatherstation/sensor.py b/homeassistant/components/trafikverket_weatherstation/sensor.py index 2dffd580b7e5a8..bf8f4c803e0392 100644 --- a/homeassistant/components/trafikverket_weatherstation/sensor.py +++ b/homeassistant/components/trafikverket_weatherstation/sensor.py @@ -1,9 +1,4 @@ -""" -Weather information for air and road temperature, provided by Trafikverket. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.trafikverket_weatherstation/ -""" +"""Weather information for air and road temperature (by Trafikverket).""" import asyncio from datetime import timedelta diff --git a/homeassistant/components/travisci/sensor.py b/homeassistant/components/travisci/sensor.py index c96bb18e95800f..99309f7e2b7533 100644 --- a/homeassistant/components/travisci/sensor.py +++ b/homeassistant/components/travisci/sensor.py @@ -1,9 +1,4 @@ -""" -This component provides HA sensor support for Travis CI framework. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.travisci/ -""" +"""This component provides HA sensor support for Travis CI framework.""" import logging from datetime import timedelta diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 0cd4a1bb6c631d..763baa262bed81 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -1,9 +1,4 @@ -""" -Provide functionality to TTS. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts/ -""" +"""Provide functionality to TTS.""" import asyncio import ctypes import functools as ft diff --git a/homeassistant/components/twilio_call/notify.py b/homeassistant/components/twilio_call/notify.py index a1a28a03b18c5d..ab57d7214656b1 100644 --- a/homeassistant/components/twilio_call/notify.py +++ b/homeassistant/components/twilio_call/notify.py @@ -1,9 +1,4 @@ -""" -Twilio Call platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.twilio_call/ -""" +"""Twilio Call platform for notify component.""" import logging import urllib diff --git a/homeassistant/components/twilio_sms/notify.py b/homeassistant/components/twilio_sms/notify.py index b3b35ea17899b9..a04e397a5688d2 100644 --- a/homeassistant/components/twilio_sms/notify.py +++ b/homeassistant/components/twilio_sms/notify.py @@ -1,9 +1,4 @@ -""" -Twilio SMS platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.twilio_sms/ -""" +"""Twilio SMS platform for notify component.""" import logging import voluptuous as vol diff --git a/homeassistant/components/twitch/sensor.py b/homeassistant/components/twitch/sensor.py index 3e00f799dcfa9c..123de752d51255 100644 --- a/homeassistant/components/twitch/sensor.py +++ b/homeassistant/components/twitch/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Twitch stream status. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.twitch/ -""" +"""Support for the Twitch stream status.""" import logging import voluptuous as vol diff --git a/homeassistant/components/twitter/notify.py b/homeassistant/components/twitter/notify.py index 9172da367856df..54cd591f394575 100644 --- a/homeassistant/components/twitter/notify.py +++ b/homeassistant/components/twitter/notify.py @@ -1,9 +1,4 @@ -""" -Twitter platform for notify component. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.twitter/ -""" +"""Twitter platform for notify component.""" from datetime import datetime, timedelta from functools import partial import json diff --git a/homeassistant/components/ubee/device_tracker.py b/homeassistant/components/ubee/device_tracker.py index f4ecc7d485543c..bbe028bbb78fc4 100644 --- a/homeassistant/components/ubee/device_tracker.py +++ b/homeassistant/components/ubee/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Ubee router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ubee/ -""" +"""Support for Ubee router.""" import logging import voluptuous as vol diff --git a/homeassistant/components/uber/sensor.py b/homeassistant/components/uber/sensor.py index a97ccaffed0ac5..87d87de66ee30e 100644 --- a/homeassistant/components/uber/sensor.py +++ b/homeassistant/components/uber/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Uber API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.uber/ -""" +"""Support for the Uber API.""" import logging from datetime import timedelta diff --git a/homeassistant/components/ubus/device_tracker.py b/homeassistant/components/ubus/device_tracker.py index 96f2f60c1e5242..54572524fb2715 100644 --- a/homeassistant/components/ubus/device_tracker.py +++ b/homeassistant/components/ubus/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for OpenWRT (ubus) routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ubus/ -""" +"""Support for OpenWRT (ubus) routers.""" import json import logging import re diff --git a/homeassistant/components/ue_smart_radio/media_player.py b/homeassistant/components/ue_smart_radio/media_player.py index 2261aadc2f68f9..0d1f17e10eca4b 100644 --- a/homeassistant/components/ue_smart_radio/media_player.py +++ b/homeassistant/components/ue_smart_radio/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Logitech UE Smart Radios. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.ue_smart_radio/ -""" +"""Support for Logitech UE Smart Radios.""" import logging diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 2dc5f7a4df3902..49e28114b17098 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Unifi WAP controllers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.unifi/ -""" +"""Support for Unifi WAP controllers.""" import logging from datetime import timedelta import voluptuous as vol diff --git a/homeassistant/components/unifi_direct/device_tracker.py b/homeassistant/components/unifi_direct/device_tracker.py index bd90099e45c9a8..29a3c58fab95ef 100644 --- a/homeassistant/components/unifi_direct/device_tracker.py +++ b/homeassistant/components/unifi_direct/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Unifi AP direct access. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.unifi_direct/ -""" +"""Support for Unifi AP direct access.""" import logging import json diff --git a/homeassistant/components/universal/media_player.py b/homeassistant/components/universal/media_player.py index 5730a0867311b3..69af20917c5676 100644 --- a/homeassistant/components/universal/media_player.py +++ b/homeassistant/components/universal/media_player.py @@ -1,9 +1,4 @@ -""" -Combination of multiple media players into one for a universal controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.universal/ -""" +"""Combination of multiple media players for a universal controller.""" from copy import copy import logging diff --git a/homeassistant/components/upc_connect/device_tracker.py b/homeassistant/components/upc_connect/device_tracker.py index 2ee6d64730d277..4a583b8349ad15 100644 --- a/homeassistant/components/upc_connect/device_tracker.py +++ b/homeassistant/components/upc_connect/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for UPC ConnectBox router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.upc_connect/ -""" +"""Support for UPC ConnectBox router.""" import asyncio import logging diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index 708ef314ab4e28..86bcee879b9dbd 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -1,9 +1,4 @@ -""" -Support for UPnP/IGD Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.upnp/ -""" +"""Support for UPnP/IGD Sensors.""" from datetime import datetime import logging diff --git a/homeassistant/components/ups/sensor.py b/homeassistant/components/ups/sensor.py index e4aab55505087d..f338e990b00fff 100644 --- a/homeassistant/components/ups/sensor.py +++ b/homeassistant/components/ups/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for UPS packages. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.ups/ -""" +"""Sensor for UPS packages.""" from collections import defaultdict import logging from datetime import timedelta diff --git a/homeassistant/components/uptime/sensor.py b/homeassistant/components/uptime/sensor.py index 197233461fb625..7e741499f73749 100644 --- a/homeassistant/components/uptime/sensor.py +++ b/homeassistant/components/uptime/sensor.py @@ -1,9 +1,4 @@ -""" -Platform to retrieve uptime for Home Assistant. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.uptime/ -""" +"""Platform to retrieve uptime for Home Assistant.""" import logging import voluptuous as vol diff --git a/homeassistant/components/uptimerobot/binary_sensor.py b/homeassistant/components/uptimerobot/binary_sensor.py index e48ac3039aeb40..8e11966b6809d4 100644 --- a/homeassistant/components/uptimerobot/binary_sensor.py +++ b/homeassistant/components/uptimerobot/binary_sensor.py @@ -1,9 +1,4 @@ -""" -A platform that to monitor Uptime Robot monitors. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/binary_sensor.uptimerobot/ -""" +"""A platform that to monitor Uptime Robot monitors.""" import logging import voluptuous as vol diff --git a/homeassistant/components/uscis/sensor.py b/homeassistant/components/uscis/sensor.py index e3a917b0a5a789..501c6c9665c909 100644 --- a/homeassistant/components/uscis/sensor.py +++ b/homeassistant/components/uscis/sensor.py @@ -1,9 +1,4 @@ -""" -Support for USCIS Case Status. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.uscis/ -""" +"""Support for USCIS Case Status.""" import logging from datetime import timedelta diff --git a/homeassistant/components/uvc/camera.py b/homeassistant/components/uvc/camera.py index 50e7c3d8fe2938..65251054060484 100644 --- a/homeassistant/components/uvc/camera.py +++ b/homeassistant/components/uvc/camera.py @@ -1,9 +1,4 @@ -""" -Support for Ubiquiti's UVC cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.uvc/ -""" +"""Support for Ubiquiti's UVC cameras.""" import logging import socket diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 3fdc7cb1a51a35..02266986ccfbef 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -1,9 +1,4 @@ -""" -Support for vacuum cleaner robots (botvacs). - -For more details about this platform, please refer to the documentation -https://home-assistant.io/components/vacuum/ -""" +"""Support for vacuum cleaner robots (botvacs).""" from datetime import timedelta from functools import partial import logging diff --git a/homeassistant/components/vasttrafik/sensor.py b/homeassistant/components/vasttrafik/sensor.py index 8148a5c2fc7bdf..d8e9f1e7675262 100644 --- a/homeassistant/components/vasttrafik/sensor.py +++ b/homeassistant/components/vasttrafik/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Västtrafik public transport. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.vasttrafik/ -""" +"""Support for Västtrafik public transport.""" from datetime import datetime from datetime import timedelta import logging diff --git a/homeassistant/components/venstar/climate.py b/homeassistant/components/venstar/climate.py index 820443ee186de3..f3e7542af5ce9f 100644 --- a/homeassistant/components/venstar/climate.py +++ b/homeassistant/components/venstar/climate.py @@ -1,9 +1,4 @@ -""" -Support for Venstar WiFi Thermostats. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.venstar/ -""" +"""Support for Venstar WiFi Thermostats.""" import logging import voluptuous as vol diff --git a/homeassistant/components/version/sensor.py b/homeassistant/components/version/sensor.py index 8a2a7593b2c01f..6982b77a51a9f5 100644 --- a/homeassistant/components/version/sensor.py +++ b/homeassistant/components/version/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor that can display the current Home Assistant versions. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.version/ -""" +"""Sensor that can display the current Home Assistant versions.""" import logging from datetime import timedelta diff --git a/homeassistant/components/vesync/switch.py b/homeassistant/components/vesync/switch.py index d9ffbf9c12d188..d37728624ef533 100644 --- a/homeassistant/components/vesync/switch.py +++ b/homeassistant/components/vesync/switch.py @@ -1,9 +1,4 @@ -""" -Support for Etekcity VeSync switches. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.vesync/ -""" +"""Support for Etekcity VeSync switches.""" import logging import voluptuous as vol from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) diff --git a/homeassistant/components/viaggiatreno/sensor.py b/homeassistant/components/viaggiatreno/sensor.py index 2b8de2042facb5..ee939d4a594df8 100644 --- a/homeassistant/components/viaggiatreno/sensor.py +++ b/homeassistant/components/viaggiatreno/sensor.py @@ -1,9 +1,4 @@ -""" -Support for information about the Italian train system using ViaggiaTreno API. - -For more details about this platform please refer to the documentation at -https://home-assistant.io/components/sensor.viaggiatreno -""" +"""Support for the Italian train system using ViaggiaTreno API.""" import asyncio import logging diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index af3fdd1e15a26f..bab54c68a908fa 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -1,9 +1,4 @@ -""" -Vizio SmartCast TV support. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.vizio/ -""" +"""Vizio SmartCast TV support.""" from datetime import timedelta import logging diff --git a/homeassistant/components/vlc/media_player.py b/homeassistant/components/vlc/media_player.py index 592243938d7670..41f9b5b16d4efa 100644 --- a/homeassistant/components/vlc/media_player.py +++ b/homeassistant/components/vlc/media_player.py @@ -1,9 +1,4 @@ -""" -Provide functionality to interact with vlc devices on the network. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.vlc/ -""" +"""Provide functionality to interact with vlc devices on the network.""" import logging import voluptuous as vol diff --git a/homeassistant/components/voicerss/tts.py b/homeassistant/components/voicerss/tts.py index 436f070e503833..d5340e45b5c442 100644 --- a/homeassistant/components/voicerss/tts.py +++ b/homeassistant/components/voicerss/tts.py @@ -1,9 +1,4 @@ -""" -Support for the voicerss speech service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts.voicerss/ -""" +"""Support for the voicerss speech service.""" import asyncio import logging diff --git a/homeassistant/components/volkszaehler/sensor.py b/homeassistant/components/volkszaehler/sensor.py index e67d9d6424ac5a..5b808ff3c38dcb 100644 --- a/homeassistant/components/volkszaehler/sensor.py +++ b/homeassistant/components/volkszaehler/sensor.py @@ -1,9 +1,4 @@ -""" -Support for consuming values for the Volkszaehler API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.volkszaehler/ -""" +"""Support for consuming values for the Volkszaehler API.""" from datetime import timedelta import logging diff --git a/homeassistant/components/vultr/binary_sensor.py b/homeassistant/components/vultr/binary_sensor.py index dccb648c9c2242..87e8e93bda7b3c 100644 --- a/homeassistant/components/vultr/binary_sensor.py +++ b/homeassistant/components/vultr/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring the state of Vultr subscriptions (VPS). - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/binary_sensor.vultr/ -""" +"""Support for monitoring the state of Vultr subscriptions (VPS).""" import logging import voluptuous as vol diff --git a/homeassistant/components/vultr/sensor.py b/homeassistant/components/vultr/sensor.py index 7ca731cabac74c..f7e03dddace4e9 100644 --- a/homeassistant/components/vultr/sensor.py +++ b/homeassistant/components/vultr/sensor.py @@ -1,9 +1,4 @@ -""" -Support for monitoring the state of Vultr Subscriptions. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/sensor.vultr/ -""" +"""Support for monitoring the state of Vultr Subscriptions.""" import logging import voluptuous as vol diff --git a/homeassistant/components/vultr/switch.py b/homeassistant/components/vultr/switch.py index 1f7d9ceaa28d23..502aaf9daa8767 100644 --- a/homeassistant/components/vultr/switch.py +++ b/homeassistant/components/vultr/switch.py @@ -1,9 +1,4 @@ -""" -Support for interacting with Vultr subscriptions. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/switch.vultr/ -""" +"""Support for interacting with Vultr subscriptions.""" import logging import voluptuous as vol diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index 16bd700e1d57c2..c81a476f0f8b2b 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -1,9 +1,4 @@ -""" -Support for wake on lan. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.wake_on_lan/ -""" +"""Support for wake on lan.""" import logging import platform import subprocess as sp diff --git a/homeassistant/components/waqi/sensor.py b/homeassistant/components/waqi/sensor.py index d6b8d278fb15d7..f3000890de6800 100644 --- a/homeassistant/components/waqi/sensor.py +++ b/homeassistant/components/waqi/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the World Air Quality Index service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.waqi/ -""" +"""Support for the World Air Quality Index service.""" import asyncio import logging from datetime import timedelta diff --git a/homeassistant/components/waterfurnace/sensor.py b/homeassistant/components/waterfurnace/sensor.py index 8a43a7dac77e2c..8b1fc46312caac 100644 --- a/homeassistant/components/waterfurnace/sensor.py +++ b/homeassistant/components/waterfurnace/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Waterfurnace. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.waterfurnace/ -""" +"""Support for Waterfurnace.""" from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.const import TEMP_FAHRENHEIT diff --git a/homeassistant/components/waze_travel_time/sensor.py b/homeassistant/components/waze_travel_time/sensor.py index 96a4c747293e9c..984a5800898dd4 100644 --- a/homeassistant/components/waze_travel_time/sensor.py +++ b/homeassistant/components/waze_travel_time/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Waze travel time sensor. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.waze_travel_time/ -""" +"""Support for Waze travel time sensor.""" from datetime import timedelta import logging diff --git a/homeassistant/components/whois/sensor.py b/homeassistant/components/whois/sensor.py index 3685652387ab88..e36bdea08c3f63 100644 --- a/homeassistant/components/whois/sensor.py +++ b/homeassistant/components/whois/sensor.py @@ -1,9 +1,4 @@ -""" -Get WHOIS information for a given host. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.whois/ -""" +"""Get WHOIS information for a given host.""" from datetime import timedelta import logging diff --git a/homeassistant/components/worldclock/sensor.py b/homeassistant/components/worldclock/sensor.py index 6bb5d1fee2ea6c..1fdf97b708816a 100644 --- a/homeassistant/components/worldclock/sensor.py +++ b/homeassistant/components/worldclock/sensor.py @@ -1,9 +1,4 @@ -""" -Support for showing the time in a different time zone. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.worldclock/ -""" +"""Support for showing the time in a different time zone.""" import logging import voluptuous as vol diff --git a/homeassistant/components/worldtidesinfo/sensor.py b/homeassistant/components/worldtidesinfo/sensor.py index 0f7bfeaa900208..1a1e349feeeef0 100644 --- a/homeassistant/components/worldtidesinfo/sensor.py +++ b/homeassistant/components/worldtidesinfo/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the worldtides.info API. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.worldtidesinfo/ -""" +"""Support for the worldtides.info API.""" from datetime import timedelta import logging import time diff --git a/homeassistant/components/worxlandroid/sensor.py b/homeassistant/components/worxlandroid/sensor.py index be5c8452d88776..fa4fcc96c120d2 100644 --- a/homeassistant/components/worxlandroid/sensor.py +++ b/homeassistant/components/worxlandroid/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Worx Landroid mower. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.worxlandroid/ -""" +"""Support for Worx Landroid mower.""" import logging import asyncio diff --git a/homeassistant/components/wsdot/sensor.py b/homeassistant/components/wsdot/sensor.py index 4e53a2c17c4b9d..3c3e9300a0268f 100644 --- a/homeassistant/components/wsdot/sensor.py +++ b/homeassistant/components/wsdot/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Washington State Department of Transportation (WSDOT) data. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.wsdot/ -""" +"""Support for Washington State Department of Transportation (WSDOT) data.""" import logging import re from datetime import datetime, timezone, timedelta diff --git a/homeassistant/components/wunderground/sensor.py b/homeassistant/components/wunderground/sensor.py index 74a4c2089b2363..7ad1a6fd75ac97 100644 --- a/homeassistant/components/wunderground/sensor.py +++ b/homeassistant/components/wunderground/sensor.py @@ -1,9 +1,4 @@ -""" -Support for WUnderground weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.wunderground/ -""" +"""Support for WUnderground weather service.""" import asyncio from datetime import timedelta import logging diff --git a/homeassistant/components/x10/light.py b/homeassistant/components/x10/light.py index 9618a13a1a9750..6c8c5f3fe6ff58 100644 --- a/homeassistant/components/x10/light.py +++ b/homeassistant/components/x10/light.py @@ -1,9 +1,4 @@ -""" -Support for X10 lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.x10/ -""" +"""Support for X10 lights.""" import logging from subprocess import check_output, CalledProcessError, STDOUT diff --git a/homeassistant/components/xbox_live/sensor.py b/homeassistant/components/xbox_live/sensor.py index 9670b4b2f9cb58..9f8a02686accb6 100644 --- a/homeassistant/components/xbox_live/sensor.py +++ b/homeassistant/components/xbox_live/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for Xbox Live account status. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.xbox_live/ -""" +"""Sensor for Xbox Live account status.""" import logging import voluptuous as vol diff --git a/homeassistant/components/xeoma/camera.py b/homeassistant/components/xeoma/camera.py index 74532a935fc984..dd0ee432707718 100644 --- a/homeassistant/components/xeoma/camera.py +++ b/homeassistant/components/xeoma/camera.py @@ -1,9 +1,4 @@ -""" -Support for Xeoma Cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.xeoma/ -""" +"""Support for Xeoma Cameras.""" import logging import voluptuous as vol diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index b0acf50ec8cd46..98e54d2bc73515 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -1,9 +1,4 @@ -""" -This component provides support for Xiaomi Cameras. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.xiaomi/ -""" +"""This component provides support for Xiaomi Cameras.""" import asyncio import logging diff --git a/homeassistant/components/xiaomi/device_tracker.py b/homeassistant/components/xiaomi/device_tracker.py index 12e64b724dd370..6c588271c9b4b6 100644 --- a/homeassistant/components/xiaomi/device_tracker.py +++ b/homeassistant/components/xiaomi/device_tracker.py @@ -1,9 +1,4 @@ -""" -Support for Xiaomi Mi routers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.xiaomi/ -""" +"""Support for Xiaomi Mi routers.""" import logging import requests diff --git a/homeassistant/components/xiaomi_tv/media_player.py b/homeassistant/components/xiaomi_tv/media_player.py index e3b25c3c31f80e..2c8a2e1ea83b70 100644 --- a/homeassistant/components/xiaomi_tv/media_player.py +++ b/homeassistant/components/xiaomi_tv/media_player.py @@ -1,9 +1,4 @@ -""" -Add support for the Xiaomi TVs. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/xiaomi_tv/ -""" +"""Add support for the Xiaomi TVs.""" import logging import voluptuous as vol diff --git a/homeassistant/components/xmpp/notify.py b/homeassistant/components/xmpp/notify.py index 5a14046bd41a3f..d8036f5ee1e43a 100644 --- a/homeassistant/components/xmpp/notify.py +++ b/homeassistant/components/xmpp/notify.py @@ -1,9 +1,4 @@ -""" -Jabber (XMPP) notification service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.xmpp/ -""" +"""Jabber (XMPP) notification service.""" from concurrent.futures import TimeoutError as FutTimeoutError import logging import mimetypes diff --git a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py index 67b74033442d35..1a8e03a6363120 100755 --- a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py +++ b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py @@ -1,9 +1,4 @@ -""" -Yale Smart Alarm client for interacting with the Yale Smart Alarm System API. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/alarm_control_panel.yale_smart_alarm -""" +"""Component for interacting with the Yale Smart Alarm System API.""" import logging import voluptuous as vol diff --git a/homeassistant/components/yamaha/media_player.py b/homeassistant/components/yamaha/media_player.py index f652d95e713af6..53c6b466f6e3a3 100644 --- a/homeassistant/components/yamaha/media_player.py +++ b/homeassistant/components/yamaha/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Yamaha Receivers. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.yamaha/ -""" +"""Support for Yamaha Receivers.""" import logging import requests diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index 6aa06b604c5d1a..94002a4cc55b27 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -1,9 +1,4 @@ -""" -Support for Yamaha MusicCast Receivers. - -For more details about this platform, please refer to the documentation at -https://www.home-assistant.io/components/media_player.yamaha_musiccast/ -""" +"""Support for Yamaha MusicCast Receivers.""" import logging import voluptuous as vol diff --git a/homeassistant/components/yandextts/tts.py b/homeassistant/components/yandextts/tts.py index e60b890e84fcb0..e08f44d1974242 100644 --- a/homeassistant/components/yandextts/tts.py +++ b/homeassistant/components/yandextts/tts.py @@ -1,9 +1,4 @@ -""" -Support for the yandex speechkit tts service. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/tts/yandextts/ -""" +"""Support for the yandex speechkit tts service.""" import asyncio import logging diff --git a/homeassistant/components/yeelightsunflower/light.py b/homeassistant/components/yeelightsunflower/light.py index 2250a85c55c962..9252143526bfa5 100644 --- a/homeassistant/components/yeelightsunflower/light.py +++ b/homeassistant/components/yeelightsunflower/light.py @@ -1,9 +1,4 @@ -""" -Support for Yeelight Sunflower color bulbs (not Yeelight Blue or WiFi). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.yeelightsunflower/ -""" +"""Support for Yeelight Sunflower color bulbs (not Yeelight Blue or WiFi).""" import logging import voluptuous as vol diff --git a/homeassistant/components/yessssms/notify.py b/homeassistant/components/yessssms/notify.py index 529aa4e7b6e6b7..c229c361e2884f 100644 --- a/homeassistant/components/yessssms/notify.py +++ b/homeassistant/components/yessssms/notify.py @@ -1,9 +1,4 @@ -""" -Support for the YesssSMS platform. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.yessssms/ -""" +"""Support for the YesssSMS platform.""" import logging import voluptuous as vol diff --git a/homeassistant/components/yi/camera.py b/homeassistant/components/yi/camera.py index f82c8c38129cb7..7ed36b97868db6 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -1,9 +1,4 @@ -""" -This component provides support for Xiaomi Cameras (HiSilicon Hi3518e V200). - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.yi/ -""" +"""Support for Xiaomi Cameras (HiSilicon Hi3518e V200).""" import asyncio import logging diff --git a/homeassistant/components/yr/sensor.py b/homeassistant/components/yr/sensor.py index 665c482f050b50..4c898a7c9fe64d 100644 --- a/homeassistant/components/yr/sensor.py +++ b/homeassistant/components/yr/sensor.py @@ -1,9 +1,4 @@ -""" -Support for Yr.no weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.yr/ -""" +"""Support for Yr.no weather service.""" import asyncio import logging diff --git a/homeassistant/components/yweather/sensor.py b/homeassistant/components/yweather/sensor.py index 349ee2c7aaee24..129532ceb57477 100644 --- a/homeassistant/components/yweather/sensor.py +++ b/homeassistant/components/yweather/sensor.py @@ -1,9 +1,4 @@ -""" -Support for the Yahoo! Weather service. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.yweather/ -""" +"""Support for the Yahoo! Weather service.""" import logging from datetime import timedelta diff --git a/homeassistant/components/zamg/sensor.py b/homeassistant/components/zamg/sensor.py index c101e4da920ecd..9ce5da6fb9566c 100644 --- a/homeassistant/components/zamg/sensor.py +++ b/homeassistant/components/zamg/sensor.py @@ -1,9 +1,4 @@ -""" -Sensor for data from Austrian "Zentralanstalt für Meteorologie und Geodynamik". - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.zamg/ -""" +"""Sensor for the Austrian "Zentralanstalt für Meteorologie und Geodynamik".""" import csv from datetime import datetime, timedelta import gzip diff --git a/homeassistant/components/zengge/light.py b/homeassistant/components/zengge/light.py index 69ca3da0af9d48..8bbd56a483ec12 100644 --- a/homeassistant/components/zengge/light.py +++ b/homeassistant/components/zengge/light.py @@ -1,9 +1,4 @@ -""" -Support for Zengge lights. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/light.zengge/ -""" +"""Support for Zengge lights.""" import logging import voluptuous as vol diff --git a/homeassistant/components/zestimate/sensor.py b/homeassistant/components/zestimate/sensor.py index ed3af84d396cc2..f69e3b16ebe584 100644 --- a/homeassistant/components/zestimate/sensor.py +++ b/homeassistant/components/zestimate/sensor.py @@ -1,9 +1,4 @@ -""" -Support for zestimate data from zillow.com. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.zestimate/ -""" +"""Support for zestimate data from zillow.com.""" from datetime import timedelta import logging diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 088ffff13d1741..08362eba082845 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -1,9 +1,4 @@ -""" -Support for Zigbee Home Automation devices. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/zha/ -""" +"""Support for Zigbee Home Automation devices.""" import logging import voluptuous as vol diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index 2f88ad3a78bdec..aacb0a711a53f1 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -1,9 +1,4 @@ -""" -Web socket API for Zigbee Home Automation devices. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/zha/ -""" +"""Web socket API for Zigbee Home Automation devices.""" import asyncio import logging diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index 7c08c758af29f5..b4254eb83e7a49 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -1,9 +1,4 @@ -""" -Binary sensors on Zigbee Home Automation networks. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/binary_sensor.zha/ -""" +"""Binary sensors on Zigbee Home Automation networks.""" import logging from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice diff --git a/homeassistant/components/zha/const.py b/homeassistant/components/zha/const.py index e7cf424990b46d..1ccc3e0ea25357 100644 --- a/homeassistant/components/zha/const.py +++ b/homeassistant/components/zha/const.py @@ -1,9 +1,4 @@ -""" -Backwards compatible constants bridge. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/fan.zha/ -""" +"""Backwards compatible constants bridge.""" # pylint: disable=W0614,W0401 from .core.const import * # noqa: F401,F403 from .core.registries import * # noqa: F401,F403 diff --git a/homeassistant/components/zha/device_entity.py b/homeassistant/components/zha/device_entity.py index 7563481bbb7b89..3937e597b784db 100644 --- a/homeassistant/components/zha/device_entity.py +++ b/homeassistant/components/zha/device_entity.py @@ -1,9 +1,4 @@ -""" -Device entity for Zigbee Home Automation. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/zha/ -""" +"""Device entity for Zigbee Home Automation.""" import logging import time diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index 1e98118e09f87c..d894ef5d7a37c4 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -1,9 +1,4 @@ -""" -Entity for Zigbee Home Automation. - -For more details about this component, please refer to the documentation at -https://home-assistant.io/components/zha/ -""" +"""Entity for Zigbee Home Automation.""" import logging import time diff --git a/homeassistant/components/zha/fan.py b/homeassistant/components/zha/fan.py index 73989ef32b4b9d..b80834af1d7d20 100644 --- a/homeassistant/components/zha/fan.py +++ b/homeassistant/components/zha/fan.py @@ -1,9 +1,4 @@ -""" -Fans on Zigbee Home Automation networks. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/fan.zha/ -""" +"""Fans on Zigbee Home Automation networks.""" import logging from homeassistant.core import callback diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 6ba4efa9b0f874..573936d6ac2a8c 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -1,9 +1,4 @@ -""" -Lights on Zigbee Home Automation networks. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/light.zha/ -""" +"""Lights on Zigbee Home Automation networks.""" from datetime import timedelta import logging diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index d45d8f8c30ddc3..13932d7dd7ab1b 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -1,9 +1,4 @@ -""" -Sensors on Zigbee Home Automation networks. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/sensor.zha/ -""" +"""Sensors on Zigbee Home Automation networks.""" import logging from homeassistant.core import callback diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index f1bf671a43d9f9..34c9ab2514d003 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -1,9 +1,4 @@ -""" -Switches on Zigbee Home Automation networks. - -For more details on this platform, please refer to the documentation -at https://home-assistant.io/components/switch.zha/ -""" +"""Switches on Zigbee Home Automation networks.""" import logging from homeassistant.components.switch import DOMAIN, SwitchDevice diff --git a/homeassistant/components/zhong_hong/climate.py b/homeassistant/components/zhong_hong/climate.py index 78cd7d16c483d5..7fd2b971009e17 100644 --- a/homeassistant/components/zhong_hong/climate.py +++ b/homeassistant/components/zhong_hong/climate.py @@ -1,9 +1,4 @@ -""" -Support for ZhongHong HVAC Controller. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/climate.zhong_hong/ -""" +"""Support for ZhongHong HVAC Controller.""" import logging import voluptuous as vol diff --git a/homeassistant/components/ziggo_mediabox_xl/media_player.py b/homeassistant/components/ziggo_mediabox_xl/media_player.py index abad22d89eb5b0..574d08e97a4504 100644 --- a/homeassistant/components/ziggo_mediabox_xl/media_player.py +++ b/homeassistant/components/ziggo_mediabox_xl/media_player.py @@ -1,9 +1,4 @@ -""" -Support for interface with a Ziggo Mediabox XL. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/media_player.ziggo_mediabox_xl/ -""" +"""Support for interface with a Ziggo Mediabox XL.""" import logging import socket From b4fc1d77ea8fdd1bf53efd222fbb28b46ffd497b Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 3 Apr 2019 09:04:30 -0700 Subject: [PATCH 376/605] Fix trend binary sensor and tests (#22686) --- .../components/trend/binary_sensor.py | 2 +- tests/components/trend/test_binary_sensor.py | 35 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/trend/binary_sensor.py b/homeassistant/components/trend/binary_sensor.py index 2b1ae0e083a614..163703373d3303 100644 --- a/homeassistant/components/trend/binary_sensor.py +++ b/homeassistant/components/trend/binary_sensor.py @@ -141,7 +141,7 @@ def trend_sensor_state_listener(entity, old_state, new_state): else: state = new_state.state if state not in (STATE_UNKNOWN, STATE_UNAVAILABLE): - sample = (utcnow().timestamp(), float(state)) + sample = (new_state.last_updated.timestamp(), float(state)) self.samples.append(sample) self.async_schedule_update_ha_state(True) except (ValueError, TypeError) as ex: diff --git a/tests/components/trend/test_binary_sensor.py b/tests/components/trend/test_binary_sensor.py index b77f9060b40696..2116382eafec93 100644 --- a/tests/components/trend/test_binary_sensor.py +++ b/tests/components/trend/test_binary_sensor.py @@ -1,5 +1,9 @@ """The test for the Trend sensor platform.""" +from datetime import timedelta +from unittest.mock import patch + from homeassistant import setup +import homeassistant.util.dt as dt_util from tests.common import get_test_home_assistant, assert_setup_component @@ -46,7 +50,7 @@ def test_up_using_trendline(self): 'sensors': { 'test_trend_sensor': { 'entity_id': "sensor.test_state", - 'sample_duration': 300, + 'sample_duration': 10000, 'min_gradient': 1, 'max_samples': 25, } @@ -54,16 +58,22 @@ def test_up_using_trendline(self): } }) - for val in [1, 0, 2, 3]: - self.hass.states.set('sensor.test_state', val) + now = dt_util.utcnow() + for val in [10, 0, 20, 30]: + with patch('homeassistant.util.dt.utcnow', return_value=now): + self.hass.states.set('sensor.test_state', val) self.hass.block_till_done() + now += timedelta(seconds=2) state = self.hass.states.get('binary_sensor.test_trend_sensor') assert state.state == 'on' - for val in [0, 1, 0, 0]: - self.hass.states.set('sensor.test_state', val) + # have to change state value, otherwise sample will lost + for val in [0, 30, 1, 0]: + with patch('homeassistant.util.dt.utcnow', return_value=now): + self.hass.states.set('sensor.test_state', val) self.hass.block_till_done() + now += timedelta(seconds=2) state = self.hass.states.get('binary_sensor.test_trend_sensor') assert state.state == 'off' @@ -76,7 +86,7 @@ def test_down_using_trendline(self): 'sensors': { 'test_trend_sensor': { 'entity_id': "sensor.test_state", - 'sample_duration': 300, + 'sample_duration': 10000, 'min_gradient': 1, 'max_samples': 25, 'invert': 'Yes' @@ -85,16 +95,21 @@ def test_down_using_trendline(self): } }) - for val in [3, 2, 3, 1]: - self.hass.states.set('sensor.test_state', val) + now = dt_util.utcnow() + for val in [30, 20, 30, 10]: + with patch('homeassistant.util.dt.utcnow', return_value=now): + self.hass.states.set('sensor.test_state', val) self.hass.block_till_done() + now += timedelta(seconds=2) state = self.hass.states.get('binary_sensor.test_trend_sensor') assert state.state == 'on' - for val in [4, 2, 4, 4]: - self.hass.states.set('sensor.test_state', val) + for val in [30, 0, 45, 50]: + with patch('homeassistant.util.dt.utcnow', return_value=now): + self.hass.states.set('sensor.test_state', val) self.hass.block_till_done() + now += timedelta(seconds=2) state = self.hass.states.get('binary_sensor.test_trend_sensor') assert state.state == 'off' From 3872ac9bf9ba061096b818a3fd61ba6f9495efdb Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 3 Apr 2019 18:05:18 +0200 Subject: [PATCH 377/605] Fix citybikes (#22683) * Move asyncio condition to instance from class, to be able to pass in lopp. * Avoid not needed sife effects in init methods. * Clean up. --- homeassistant/components/citybikes/sensor.py | 89 +++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/citybikes/sensor.py b/homeassistant/components/citybikes/sensor.py index bcf6fb923f9b52..344311aa231dd4 100644 --- a/homeassistant/components/citybikes/sensor.py +++ b/homeassistant/components/citybikes/sensor.py @@ -49,6 +49,8 @@ CITYBIKES_ATTRIBUTION = "Information provided by the CityBikes Project "\ "(https://citybik.es/#about)" +CITYBIKES_NETWORKS = 'citybikes_networks' + PLATFORM_SCHEMA = vol.All( cv.has_at_least_one_key(CONF_RADIUS, CONF_STATIONS_LIST), PLATFORM_SCHEMA.extend({ @@ -67,12 +69,12 @@ vol.Required(ATTR_LOCATION): vol.Schema({ vol.Required(ATTR_LATITUDE): cv.latitude, vol.Required(ATTR_LONGITUDE): cv.longitude, - }, extra=vol.REMOVE_EXTRA), - }, extra=vol.REMOVE_EXTRA) + }, extra=vol.REMOVE_EXTRA), +}, extra=vol.REMOVE_EXTRA) NETWORKS_RESPONSE_SCHEMA = vol.Schema({ vol.Required(ATTR_NETWORKS_LIST): [NETWORK_SCHEMA], - }) +}) STATION_SCHEMA = vol.Schema({ vol.Required(ATTR_FREE_BIKES): cv.positive_int, @@ -84,13 +86,13 @@ vol.Required(ATTR_TIMESTAMP): cv.string, vol.Optional(ATTR_EXTRA): vol.Schema({vol.Optional(ATTR_UID): cv.string}, extra=vol.REMOVE_EXTRA) - }, extra=vol.REMOVE_EXTRA) +}, extra=vol.REMOVE_EXTRA) STATIONS_RESPONSE_SCHEMA = vol.Schema({ vol.Required(ATTR_NETWORK): vol.Schema({ vol.Required(ATTR_STATIONS_LIST): [STATION_SCHEMA] - }, extra=vol.REMOVE_EXTRA) - }) + }, extra=vol.REMOVE_EXTRA) +}) class CityBikesRequestError(Exception): @@ -130,18 +132,21 @@ async def async_setup_platform(hass, config, async_add_entities, network_id = config.get(CONF_NETWORK) stations_list = set(config.get(CONF_STATIONS_LIST, [])) radius = config.get(CONF_RADIUS, 0) - name = config.get(CONF_NAME) + name = config[CONF_NAME] if not hass.config.units.is_metric: radius = distance.convert(radius, LENGTH_FEET, LENGTH_METERS) + # Create a single instance of CityBikesNetworks. + networks = hass.data.setdefault( + CITYBIKES_NETWORKS, CityBikesNetworks(hass)) + if not network_id: - network_id = await CityBikesNetwork.get_closest_network_id( - hass, latitude, longitude) + network_id = await networks.get_closest_network_id(latitude, longitude) if network_id not in hass.data[PLATFORM][MONITORED_NETWORKS]: network = CityBikesNetwork(hass, network_id) hass.data[PLATFORM][MONITORED_NETWORKS][network_id] = network - hass.async_add_job(network.async_refresh) + hass.async_create_task(network.async_refresh()) async_track_time_interval(hass, network.async_refresh, SCAN_INTERVAL) else: network = hass.data[PLATFORM][MONITORED_NETWORKS][network_id] @@ -158,29 +163,37 @@ async def async_setup_platform(hass, config, async_add_entities, if radius > dist or stations_list.intersection( (station_id, station_uid)): - devices.append(CityBikesStation(hass, network, station_id, name)) + if name: + uid = "_".join([network.network_id, name, station_id]) + else: + uid = "_".join([network.network_id, station_id]) + entity_id = async_generate_entity_id( + ENTITY_ID_FORMAT, uid, hass=hass) + devices.append(CityBikesStation(network, station_id, entity_id)) async_add_entities(devices, True) -class CityBikesNetwork: - """Thin wrapper around a CityBikes network object.""" +class CityBikesNetworks: + """Represent all CityBikes networks.""" - NETWORKS_LIST = None - NETWORKS_LIST_LOADING = asyncio.Condition() + def __init__(self, hass): + """Initialize the networks instance.""" + self.hass = hass + self.networks = None + self.networks_loading = asyncio.Condition(loop=hass.loop) - @classmethod - async def get_closest_network_id(cls, hass, latitude, longitude): + async def get_closest_network_id(self, latitude, longitude): """Return the id of the network closest to provided location.""" try: - await cls.NETWORKS_LIST_LOADING.acquire() - if cls.NETWORKS_LIST is None: + await self.networks_loading.acquire() + if self.networks is None: networks = await async_citybikes_request( - hass, NETWORKS_URI, NETWORKS_RESPONSE_SCHEMA) - cls.NETWORKS_LIST = networks[ATTR_NETWORKS_LIST] + self.hass, NETWORKS_URI, NETWORKS_RESPONSE_SCHEMA) + self.networks = networks[ATTR_NETWORKS_LIST] result = None minimum_dist = None - for network in cls.NETWORKS_LIST: + for network in self.networks: network_latitude = network[ATTR_LOCATION][ATTR_LATITUDE] network_longitude = network[ATTR_LOCATION][ATTR_LONGITUDE] dist = location.distance( @@ -193,14 +206,18 @@ async def get_closest_network_id(cls, hass, latitude, longitude): except CityBikesRequestError: raise PlatformNotReady finally: - cls.NETWORKS_LIST_LOADING.release() + self.networks_loading.release() + + +class CityBikesNetwork: + """Thin wrapper around a CityBikes network object.""" def __init__(self, hass, network_id): """Initialize the network object.""" self.hass = hass self.network_id = network_id self.stations = [] - self.ready = asyncio.Event() + self.ready = asyncio.Event(loop=hass.loop) async def async_refresh(self, now=None): """Refresh the state of the network.""" @@ -220,37 +237,29 @@ async def async_refresh(self, now=None): class CityBikesStation(Entity): """CityBikes API Sensor.""" - def __init__(self, hass, network, station_id, base_name=''): + def __init__(self, network, station_id, entity_id): """Initialize the sensor.""" self._network = network self._station_id = station_id self._station_data = {} - if base_name: - uid = "_".join([network.network_id, base_name, station_id]) - else: - uid = "_".join([network.network_id, station_id]) - self.entity_id = async_generate_entity_id( - ENTITY_ID_FORMAT, uid, hass=hass) + self.entity_id = entity_id @property def state(self): """Return the state of the sensor.""" - return self._station_data.get(ATTR_FREE_BIKES, None) + return self._station_data.get(ATTR_FREE_BIKES) @property def name(self): """Return the name of the sensor.""" - if ATTR_NAME in self._station_data: - return self._station_data[ATTR_NAME] - return None + return self._station_data.get(ATTR_NAME) async def async_update(self): """Update station state.""" - if self._network.ready.is_set(): - for station in self._network.stations: - if station[ATTR_ID] == self._station_id: - self._station_data = station - break + for station in self._network.stations: + if station[ATTR_ID] == self._station_id: + self._station_data = station + break @property def device_state_attributes(self): From 14da2fd8c9001cf6343c6bc41a3aefecac373e71 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 10:20:56 -0700 Subject: [PATCH 378/605] Google Assistant: Add support for open/close binary sensors (#22674) * Google Assistant: Add support for binary sensors * Update test --- .../components/binary_sensor/__init__.py | 116 ++++++++++++++---- .../components/google_assistant/smart_home.py | 5 +- .../components/google_assistant/trait.py | 49 +++++--- .../components/google_assistant/test_trait.py | 110 ++++++++++++----- 4 files changed, 207 insertions(+), 73 deletions(-) diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index 029ed8faa6bbee..19054588ee7dd9 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -15,30 +15,100 @@ SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_FORMAT = DOMAIN + '.{}' + +# On means low, Off means normal +DEVICE_CLASS_BATTERY = 'battery' + +# On means cold, Off means normal +DEVICE_CLASS_COLD = 'cold' + +# On means connected, Off means disconnected +DEVICE_CLASS_CONNECTIVITY = 'connectivity' + +# On means open, Off means closed +DEVICE_CLASS_DOOR = 'door' + +# On means open, Off means closed +DEVICE_CLASS_GARAGE_DOOR = 'garage_door' + +# On means gas detected, Off means no gas (clear) +DEVICE_CLASS_GAS = 'gas' + +# On means hot, Off means normal +DEVICE_CLASS_HEAT = 'heat' + +# On means light detected, Off means no light +DEVICE_CLASS_LIGHT = 'light' + +# On means open (unlocked), Off means closed (locked) +DEVICE_CLASS_LOCK = 'lock' + +# On means wet, Off means dry +DEVICE_CLASS_MOISTURE = 'moisture' + +# On means motion detected, Off means no motion (clear) +DEVICE_CLASS_MOTION = 'motion' + +# On means moving, Off means not moving (stopped) +DEVICE_CLASS_MOVING = 'moving' + +# On means occupied, Off means not occupied (clear) +DEVICE_CLASS_OCCUPANCY = 'occupancy' + +# On means open, Off means closed +DEVICE_CLASS_OPENING = 'opening' + +# On means plugged in, Off means unplugged +DEVICE_CLASS_PLUG = 'plug' + +# On means power detected, Off means no power +DEVICE_CLASS_POWER = 'power' + +# On means home, Off means away +DEVICE_CLASS_PRESENCE = 'presence' + +# On means problem detected, Off means no problem (OK) +DEVICE_CLASS_PROBLEM = 'problem' + +# On means unsafe, Off means safe +DEVICE_CLASS_SAFETY = 'safety' + +# On means smoke detected, Off means no smoke (clear) +DEVICE_CLASS_SMOKE = 'smoke' + +# On means sound detected, Off means no sound (clear) +DEVICE_CLASS_SOUND = 'sound' + +# On means vibration detected, Off means no vibration +DEVICE_CLASS_VIBRATION = 'vibration' + +# On means open, Off means closed +DEVICE_CLASS_WINDOW = 'window' + DEVICE_CLASSES = [ - 'battery', # On means low, Off means normal - 'cold', # On means cold, Off means normal - 'connectivity', # On means connected, Off means disconnected - 'door', # On means open, Off means closed - 'garage_door', # On means open, Off means closed - 'gas', # On means gas detected, Off means no gas (clear) - 'heat', # On means hot, Off means normal - 'light', # On means light detected, Off means no light - 'lock', # On means open (unlocked), Off means closed (locked) - 'moisture', # On means wet, Off means dry - 'motion', # On means motion detected, Off means no motion (clear) - 'moving', # On means moving, Off means not moving (stopped) - 'occupancy', # On means occupied, Off means not occupied (clear) - 'opening', # On means open, Off means closed - 'plug', # On means plugged in, Off means unplugged - 'power', # On means power detected, Off means no power - 'presence', # On means home, Off means away - 'problem', # On means problem detected, Off means no problem (OK) - 'safety', # On means unsafe, Off means safe - 'smoke', # On means smoke detected, Off means no smoke (clear) - 'sound', # On means sound detected, Off means no sound (clear) - 'vibration', # On means vibration detected, Off means no vibration - 'window', # On means open, Off means closed + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_COLD, + DEVICE_CLASS_CONNECTIVITY, + DEVICE_CLASS_DOOR, + DEVICE_CLASS_GARAGE_DOOR, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HEAT, + DEVICE_CLASS_LIGHT, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, + DEVICE_CLASS_MOVING, + DEVICE_CLASS_OCCUPANCY, + DEVICE_CLASS_OPENING, + DEVICE_CLASS_PLUG, + DEVICE_CLASS_POWER, + DEVICE_CLASS_PRESENCE, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_SAFETY, + DEVICE_CLASS_SMOKE, + DEVICE_CLASS_SOUND, + DEVICE_CLASS_VIBRATION, + DEVICE_CLASS_WINDOW, ] DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index d84c8037c60bef..cf31e3423a72a3 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -9,7 +9,7 @@ from homeassistant.core import callback from homeassistant.const import ( CLOUD_NEVER_EXPOSED_ENTITIES, CONF_NAME, STATE_UNAVAILABLE, - ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, + ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, ATTR_DEVICE_CLASS, ) from homeassistant.components import ( camera, @@ -92,10 +92,11 @@ def traits(self): state = self.state domain = state.domain features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + device_class = state.attributes.get(ATTR_DEVICE_CLASS) self._traits = [Trait(self.hass, state, self.config) for Trait in trait.TRAITS - if Trait.supported(domain, features)] + if Trait.supported(domain, features, device_class)] return self._traits async def sync_serialize(self): diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index de3a9530b5035a..41549c021fe547 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -2,6 +2,7 @@ import logging from homeassistant.components import ( + binary_sensor, camera, cover, group, @@ -127,7 +128,7 @@ class BrightnessTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain == light.DOMAIN: return features & light.SUPPORT_BRIGHTNESS @@ -193,7 +194,7 @@ class CameraStreamTrait(_Trait): stream_info = None @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain == camera.DOMAIN: return features & camera.SUPPORT_STREAM @@ -236,7 +237,7 @@ class OnOffTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" return domain in ( group.DOMAIN, @@ -285,7 +286,7 @@ class ColorSpectrumTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain != light.DOMAIN: return False @@ -341,7 +342,7 @@ class ColorTemperatureTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain != light.DOMAIN: return False @@ -414,7 +415,7 @@ class SceneTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" return domain in (scene.DOMAIN, script.DOMAIN) @@ -450,7 +451,7 @@ class DockTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" return domain == vacuum.DOMAIN @@ -484,7 +485,7 @@ class StartStopTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" return domain == vacuum.DOMAIN @@ -554,7 +555,7 @@ class TemperatureSettingTrait(_Trait): google_to_hass = {value: key for key, value in hass_to_google.items()} @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain != climate.DOMAIN: return False @@ -739,7 +740,7 @@ class LockUnlockTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" return domain == lock.DOMAIN @@ -790,7 +791,7 @@ class FanSpeedTrait(_Trait): } @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain != fan.DOMAIN: return False @@ -941,7 +942,7 @@ class ModesTrait(_Trait): } @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" if domain != media_player.DOMAIN: return False @@ -1042,13 +1043,25 @@ class OpenCloseTrait(_Trait): ] @staticmethod - def supported(domain, features): + def supported(domain, features, device_class): """Test if state is supported.""" - return domain == cover.DOMAIN + if domain == cover.DOMAIN: + return True + + return domain == binary_sensor.DOMAIN and device_class in ( + binary_sensor.DEVICE_CLASS_DOOR, + binary_sensor.DEVICE_CLASS_GARAGE_DOOR, + binary_sensor.DEVICE_CLASS_LOCK, + binary_sensor.DEVICE_CLASS_OPENING, + binary_sensor.DEVICE_CLASS_WINDOW, + ) def sync_attributes(self): """Return opening direction.""" - return {} + attrs = {} + if self.state.domain == binary_sensor.DOMAIN: + attrs['queryOnlyOpenClose'] = True + return attrs def query_attributes(self): """Return state query attributes.""" @@ -1073,6 +1086,12 @@ def query_attributes(self): else: response['openPercent'] = 0 + elif domain == binary_sensor.DOMAIN: + if self.state.state == STATE_ON: + response['openPercent'] = 100 + else: + response['openPercent'] = 0 + return response async def execute(self, command, data, params): diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 81a7fbe1bf7a4f..d85fc692cb9751 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -4,6 +4,7 @@ import pytest from homeassistant.components import ( + binary_sensor, camera, cover, fan, @@ -22,7 +23,7 @@ from homeassistant.const import ( STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, - ATTR_ASSUMED_STATE) + ATTR_DEVICE_CLASS, ATTR_ASSUMED_STATE) from homeassistant.core import State, DOMAIN as HA_DOMAIN, EVENT_CALL_SERVICE from homeassistant.util import color from tests.common import async_mock_service, mock_coro @@ -49,7 +50,7 @@ async def test_brightness_light(hass): """Test brightness trait support for light domain.""" assert trait.BrightnessTrait.supported(light.DOMAIN, - light.SUPPORT_BRIGHTNESS) + light.SUPPORT_BRIGHTNESS, None) trt = trait.BrightnessTrait(hass, State('light.bla', light.STATE_ON, { light.ATTR_BRIGHTNESS: 243 @@ -87,7 +88,8 @@ async def test_brightness_light(hass): async def test_brightness_media_player(hass): """Test brightness trait support for media player domain.""" assert trait.BrightnessTrait.supported(media_player.DOMAIN, - media_player.SUPPORT_VOLUME_SET) + media_player.SUPPORT_VOLUME_SET, + None) trt = trait.BrightnessTrait(hass, State( 'media_player.bla', media_player.STATE_PLAYING, { @@ -116,7 +118,7 @@ async def test_camera_stream(hass): """Test camera stream trait support for camera domain.""" hass.config.api = Mock(base_url='http://1.1.1.1:8123') assert trait.CameraStreamTrait.supported(camera.DOMAIN, - camera.SUPPORT_STREAM) + camera.SUPPORT_STREAM, None) trt = trait.CameraStreamTrait( hass, State('camera.bla', camera.STATE_IDLE, {}), BASIC_CONFIG @@ -143,7 +145,7 @@ async def test_camera_stream(hass): async def test_onoff_group(hass): """Test OnOff trait support for group domain.""" - assert trait.OnOffTrait.supported(group.DOMAIN, 0) + assert trait.OnOffTrait.supported(group.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('group.bla', STATE_ON), BASIC_CONFIG) @@ -181,7 +183,7 @@ async def test_onoff_group(hass): async def test_onoff_input_boolean(hass): """Test OnOff trait support for input_boolean domain.""" - assert trait.OnOffTrait.supported(input_boolean.DOMAIN, 0) + assert trait.OnOffTrait.supported(input_boolean.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('input_boolean.bla', STATE_ON), BASIC_CONFIG) @@ -221,7 +223,7 @@ async def test_onoff_input_boolean(hass): async def test_onoff_switch(hass): """Test OnOff trait support for switch domain.""" - assert trait.OnOffTrait.supported(switch.DOMAIN, 0) + assert trait.OnOffTrait.supported(switch.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('switch.bla', STATE_ON), BASIC_CONFIG) @@ -260,7 +262,7 @@ async def test_onoff_switch(hass): async def test_onoff_fan(hass): """Test OnOff trait support for fan domain.""" - assert trait.OnOffTrait.supported(fan.DOMAIN, 0) + assert trait.OnOffTrait.supported(fan.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('fan.bla', STATE_ON), BASIC_CONFIG) @@ -296,7 +298,7 @@ async def test_onoff_fan(hass): async def test_onoff_light(hass): """Test OnOff trait support for light domain.""" - assert trait.OnOffTrait.supported(light.DOMAIN, 0) + assert trait.OnOffTrait.supported(light.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('light.bla', STATE_ON), BASIC_CONFIG) @@ -334,7 +336,7 @@ async def test_onoff_light(hass): async def test_onoff_media_player(hass): """Test OnOff trait support for media_player domain.""" - assert trait.OnOffTrait.supported(media_player.DOMAIN, 0) + assert trait.OnOffTrait.supported(media_player.DOMAIN, 0, None) trt_on = trait.OnOffTrait(hass, State('media_player.bla', STATE_ON), BASIC_CONFIG) @@ -376,12 +378,12 @@ async def test_onoff_media_player(hass): async def test_onoff_climate(hass): """Test OnOff trait not supported for climate domain.""" assert not trait.OnOffTrait.supported( - climate.DOMAIN, climate.SUPPORT_ON_OFF) + climate.DOMAIN, climate.SUPPORT_ON_OFF, None) async def test_dock_vacuum(hass): """Test dock trait support for vacuum domain.""" - assert trait.DockTrait.supported(vacuum.DOMAIN, 0) + assert trait.DockTrait.supported(vacuum.DOMAIN, 0, None) trt = trait.DockTrait(hass, State('vacuum.bla', vacuum.STATE_IDLE), BASIC_CONFIG) @@ -404,7 +406,7 @@ async def test_dock_vacuum(hass): async def test_startstop_vacuum(hass): """Test startStop trait support for vacuum domain.""" - assert trait.StartStopTrait.supported(vacuum.DOMAIN, 0) + assert trait.StartStopTrait.supported(vacuum.DOMAIN, 0, None) trt = trait.StartStopTrait(hass, State('vacuum.bla', vacuum.STATE_PAUSED, { ATTR_SUPPORTED_FEATURES: vacuum.SUPPORT_PAUSE, @@ -452,9 +454,9 @@ async def test_startstop_vacuum(hass): async def test_color_spectrum_light(hass): """Test ColorSpectrum trait support for light domain.""" - assert not trait.ColorSpectrumTrait.supported(light.DOMAIN, 0) + assert not trait.ColorSpectrumTrait.supported(light.DOMAIN, 0, None) assert trait.ColorSpectrumTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR) + light.SUPPORT_COLOR, None) trt = trait.ColorSpectrumTrait(hass, State('light.bla', STATE_ON, { light.ATTR_HS_COLOR: (0, 94), @@ -496,9 +498,10 @@ async def test_color_spectrum_light(hass): async def test_color_temperature_light(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0) + assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP) + light.SUPPORT_COLOR_TEMP, + None) trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, @@ -552,9 +555,10 @@ async def test_color_temperature_light(hass): async def test_color_temperature_light_bad_temp(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0) + assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP) + light.SUPPORT_COLOR_TEMP, + None) trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, @@ -568,7 +572,7 @@ async def test_color_temperature_light_bad_temp(hass): async def test_scene_scene(hass): """Test Scene trait support for scene domain.""" - assert trait.SceneTrait.supported(scene.DOMAIN, 0) + assert trait.SceneTrait.supported(scene.DOMAIN, 0, None) trt = trait.SceneTrait(hass, State('scene.bla', scene.STATE), BASIC_CONFIG) assert trt.sync_attributes() == {} @@ -585,7 +589,7 @@ async def test_scene_scene(hass): async def test_scene_script(hass): """Test Scene trait support for script domain.""" - assert trait.SceneTrait.supported(script.DOMAIN, 0) + assert trait.SceneTrait.supported(script.DOMAIN, 0, None) trt = trait.SceneTrait(hass, State('script.bla', STATE_OFF), BASIC_CONFIG) assert trt.sync_attributes() == {} @@ -606,9 +610,9 @@ async def test_scene_script(hass): async def test_temperature_setting_climate_onoff(hass): """Test TemperatureSetting trait support for climate domain - range.""" - assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) + assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0, None) assert trait.TemperatureSettingTrait.supported( - climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) + climate.DOMAIN, climate.SUPPORT_OPERATION_MODE, None) hass.config.units.temperature_unit = TEMP_FAHRENHEIT @@ -650,9 +654,9 @@ async def test_temperature_setting_climate_onoff(hass): async def test_temperature_setting_climate_range(hass): """Test TemperatureSetting trait support for climate domain - range.""" - assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) + assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0, None) assert trait.TemperatureSettingTrait.supported( - climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) + climate.DOMAIN, climate.SUPPORT_OPERATION_MODE, None) hass.config.units.temperature_unit = TEMP_FAHRENHEIT @@ -725,9 +729,9 @@ async def test_temperature_setting_climate_range(hass): async def test_temperature_setting_climate_setpoint(hass): """Test TemperatureSetting trait support for climate domain - setpoint.""" - assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0) + assert not trait.TemperatureSettingTrait.supported(climate.DOMAIN, 0, None) assert trait.TemperatureSettingTrait.supported( - climate.DOMAIN, climate.SUPPORT_OPERATION_MODE) + climate.DOMAIN, climate.SUPPORT_OPERATION_MODE, None) hass.config.units.temperature_unit = TEMP_CELSIUS @@ -825,7 +829,8 @@ async def test_temperature_setting_climate_setpoint_auto(hass): async def test_lock_unlock_lock(hass): """Test LockUnlock trait locking support for lock domain.""" - assert trait.LockUnlockTrait.supported(lock.DOMAIN, lock.SUPPORT_OPEN) + assert trait.LockUnlockTrait.supported(lock.DOMAIN, lock.SUPPORT_OPEN, + None) trt = trait.LockUnlockTrait(hass, State('lock.front_door', lock.STATE_UNLOCKED), @@ -850,7 +855,8 @@ async def test_lock_unlock_lock(hass): async def test_lock_unlock_unlock(hass): """Test LockUnlock trait unlocking support for lock domain.""" - assert trait.LockUnlockTrait.supported(lock.DOMAIN, lock.SUPPORT_OPEN) + assert trait.LockUnlockTrait.supported(lock.DOMAIN, lock.SUPPORT_OPEN, + None) trt = trait.LockUnlockTrait(hass, State('lock.front_door', lock.STATE_LOCKED), @@ -887,7 +893,8 @@ async def test_lock_unlock_unlock(hass): async def test_fan_speed(hass): """Test FanSpeed trait speed control support for fan domain.""" - assert trait.FanSpeedTrait.supported(fan.DOMAIN, fan.SUPPORT_SET_SPEED) + assert trait.FanSpeedTrait.supported(fan.DOMAIN, fan.SUPPORT_SET_SPEED, + None) trt = trait.FanSpeedTrait( hass, State( @@ -970,7 +977,7 @@ async def test_fan_speed(hass): async def test_modes(hass): """Test Mode trait.""" assert trait.ModesTrait.supported( - media_player.DOMAIN, media_player.SUPPORT_SELECT_SOURCE) + media_player.DOMAIN, media_player.SUPPORT_SELECT_SOURCE, None) trt = trait.ModesTrait( hass, State( @@ -1056,9 +1063,9 @@ async def test_modes(hass): async def test_openclose_cover(hass): - """Test cover trait.""" + """Test OpenClose trait support for cover domain.""" assert trait.OpenCloseTrait.supported(cover.DOMAIN, - cover.SUPPORT_SET_POSITION) + cover.SUPPORT_SET_POSITION, None) # No position trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { @@ -1098,3 +1105,40 @@ async def test_openclose_cover(hass): ATTR_ENTITY_ID: 'cover.bla', cover.ATTR_POSITION: 50 } + + +@pytest.mark.parametrize('device_class', ( + binary_sensor.DEVICE_CLASS_DOOR, + binary_sensor.DEVICE_CLASS_GARAGE_DOOR, + binary_sensor.DEVICE_CLASS_LOCK, + binary_sensor.DEVICE_CLASS_OPENING, + binary_sensor.DEVICE_CLASS_WINDOW, +)) +async def test_openclose_binary_sensor(hass, device_class): + """Test OpenClose trait support for binary_sensor domain.""" + assert trait.OpenCloseTrait.supported(binary_sensor.DOMAIN, + 0, device_class) + + trt = trait.OpenCloseTrait(hass, State('binary_sensor.test', STATE_ON, { + ATTR_DEVICE_CLASS: device_class, + }), BASIC_CONFIG) + + assert trt.sync_attributes() == { + 'queryOnlyOpenClose': True, + } + + assert trt.query_attributes() == { + 'openPercent': 100 + } + + trt = trait.OpenCloseTrait(hass, State('binary_sensor.test', STATE_OFF, { + ATTR_DEVICE_CLASS: device_class, + }), BASIC_CONFIG) + + assert trt.sync_attributes() == { + 'queryOnlyOpenClose': True, + } + + assert trt.query_attributes() == { + 'openPercent': 0 + } From 51c7cbc6b93a8bb2ed2acdba84897e15af01c3a2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 3 Apr 2019 05:21:25 -0700 Subject: [PATCH 379/605] Add mobile_app notify platform (#22580) * Add mobile_app notify platform * Requested changes * Fix incorrect param for status code * Move push_registrations to notify platform file * Trim down registration information sent in push * quotes * Use async version of load_platform * Add warning for duplicate device names * Switch to async_get_service * add mobile_app.notify test * Update tests/components/mobile_app/test_notify.py * Update tests/components/mobile_app/test_notify.py --- .../components/mobile_app/__init__.py | 13 +- homeassistant/components/mobile_app/const.py | 2 + homeassistant/components/mobile_app/notify.py | 134 ++++++++++++++++++ tests/components/mobile_app/test_notify.py | 81 +++++++++++ 4 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/mobile_app/notify.py create mode 100644 tests/components/mobile_app/test_notify.py diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index ecbe8d708476e7..a4ae78959cf367 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -2,13 +2,13 @@ from homeassistant import config_entries from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.components.webhook import async_register as webhook_register -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, discovery from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, ATTR_MANUFACTURER, - ATTR_MODEL, ATTR_OS_VERSION, DATA_BINARY_SENSOR, - DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_DEVICES, - DATA_SENSOR, DATA_STORE, DOMAIN, STORAGE_KEY, +from .const import (ATTR_DEVICE_ID, ATTR_DEVICE_NAME, + ATTR_MANUFACTURER, ATTR_MODEL, ATTR_OS_VERSION, + DATA_BINARY_SENSOR, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, + DATA_DEVICES, DATA_SENSOR, DATA_STORE, DOMAIN, STORAGE_KEY, STORAGE_VERSION) from .http_api import RegistrationsView @@ -52,6 +52,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): except ValueError: pass + hass.async_create_task(discovery.async_load_platform( + hass, 'notify', DOMAIN, {}, config)) + return True diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 3aa4626da29c41..61c50e97c6e838 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -40,6 +40,8 @@ ATTR_MODEL = 'model' ATTR_OS_NAME = 'os_name' ATTR_OS_VERSION = 'os_version' +ATTR_PUSH_TOKEN = 'push_token' +ATTR_PUSH_URL = 'push_url' ATTR_SUPPORTS_ENCRYPTION = 'supports_encryption' ATTR_EVENT_DATA = 'event_data' diff --git a/homeassistant/components/mobile_app/notify.py b/homeassistant/components/mobile_app/notify.py new file mode 100644 index 00000000000000..0120b1a6ffbac2 --- /dev/null +++ b/homeassistant/components/mobile_app/notify.py @@ -0,0 +1,134 @@ +"""Support for mobile_app push notifications.""" +import asyncio +from datetime import datetime, timezone +import logging + +import async_timeout + +from homeassistant.components.notify import ( + ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, + BaseNotificationService) +from homeassistant.components.mobile_app.const import ( + ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, ATTR_DEVICE_NAME, + ATTR_OS_VERSION, ATTR_PUSH_TOKEN, ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, + DOMAIN) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.util.dt as dt_util + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['mobile_app'] + + +def push_registrations(hass): + """Return a dictionary of push enabled registrations.""" + targets = {} + for webhook_id, entry in hass.data[DOMAIN][DATA_CONFIG_ENTRIES].items(): + data = entry.data + app_data = data[ATTR_APP_DATA] + if ATTR_PUSH_TOKEN in app_data and ATTR_PUSH_URL in app_data: + device_name = data[ATTR_DEVICE_NAME] + if device_name in targets: + _LOGGER.warning("Found duplicate device name %s", device_name) + continue + targets[device_name] = webhook_id + return targets + + +# pylint: disable=invalid-name +def log_rate_limits(hass, device_name, resp, level=logging.INFO): + """Output rate limit log line at given level.""" + rate_limits = resp['rateLimits'] + resetsAt = dt_util.parse_datetime(rate_limits['resetsAt']) + resetsAtTime = resetsAt - datetime.now(timezone.utc) + rate_limit_msg = ("mobile_app push notification rate limits for %s: " + "%d sent, %d allowed, %d errors, " + "resets in %s") + _LOGGER.log(level, rate_limit_msg, + device_name, + rate_limits['successful'], + rate_limits['maximum'], rate_limits['errors'], + str(resetsAtTime).split(".")[0]) + + +async def async_get_service(hass, config, discovery_info=None): + """Get the mobile_app notification service.""" + session = async_get_clientsession(hass) + return MobileAppNotificationService(session) + + +class MobileAppNotificationService(BaseNotificationService): + """Implement the notification service for mobile_app.""" + + def __init__(self, session): + """Initialize the service.""" + self._session = session + + @property + def targets(self): + """Return a dictionary of registered targets.""" + return push_registrations(self.hass) + + async def async_send_message(self, message="", **kwargs): + """Send a message to the Lambda APNS gateway.""" + data = {ATTR_MESSAGE: message} + + if kwargs.get(ATTR_TITLE) is not None: + # Remove default title from notifications. + if kwargs.get(ATTR_TITLE) != ATTR_TITLE_DEFAULT: + data[ATTR_TITLE] = kwargs.get(ATTR_TITLE) + + targets = kwargs.get(ATTR_TARGET) + + if not targets: + targets = push_registrations(self.hass) + + if kwargs.get(ATTR_DATA) is not None: + data[ATTR_DATA] = kwargs.get(ATTR_DATA) + + for target in targets: + + entry = self.hass.data[DOMAIN][DATA_CONFIG_ENTRIES][target] + entry_data = entry.data + + app_data = entry_data[ATTR_APP_DATA] + push_token = app_data[ATTR_PUSH_TOKEN] + push_url = app_data[ATTR_PUSH_URL] + + data[ATTR_PUSH_TOKEN] = push_token + + reg_info = { + ATTR_APP_ID: entry_data[ATTR_APP_ID], + ATTR_APP_VERSION: entry_data[ATTR_APP_VERSION], + } + if ATTR_OS_VERSION in entry_data: + reg_info[ATTR_OS_VERSION] = entry_data[ATTR_OS_VERSION] + + data['registration_info'] = reg_info + + try: + with async_timeout.timeout(10, loop=self.hass.loop): + response = await self._session.post(push_url, json=data) + result = await response.json() + + if response.status == 201: + log_rate_limits(self.hass, + entry_data[ATTR_DEVICE_NAME], result) + return + + fallback_error = result.get("errorMessage", + "Unknown error") + fallback_message = ("Internal server error, " + "please try again later: " + "{}").format(fallback_error) + message = result.get("message", fallback_message) + if response.status == 429: + _LOGGER.warning(message) + log_rate_limits(self.hass, + entry_data[ATTR_DEVICE_NAME], + result, logging.WARNING) + else: + _LOGGER.error(message) + + except asyncio.TimeoutError: + _LOGGER.error("Timeout sending notification to %s", push_url) diff --git a/tests/components/mobile_app/test_notify.py b/tests/components/mobile_app/test_notify.py new file mode 100644 index 00000000000000..395dee6c11700f --- /dev/null +++ b/tests/components/mobile_app/test_notify.py @@ -0,0 +1,81 @@ +"""Notify platform tests for mobile_app.""" +# pylint: disable=redefined-outer-name +import pytest + +from homeassistant.setup import async_setup_component + +from homeassistant.components.mobile_app.const import DOMAIN + +from tests.common import MockConfigEntry + + +@pytest.fixture +async def setup_push_receiver(hass, aioclient_mock): + """Fixture that sets up a mocked push receiver.""" + push_url = 'https://mobile-push.home-assistant.dev/push' + + from datetime import datetime, timedelta + now = (datetime.now() + timedelta(hours=24)) + iso_time = now.strftime("%Y-%m-%dT%H:%M:%SZ") + + aioclient_mock.post(push_url, json={ + 'rateLimits': { + 'attempts': 1, + 'successful': 1, + 'errors': 0, + 'total': 1, + 'maximum': 150, + 'remaining': 149, + 'resetsAt': iso_time + } + }) + + entry = MockConfigEntry( + connection_class="cloud_push", + data={ + "app_data": { + "push_token": "PUSH_TOKEN", + "push_url": push_url + }, + "app_id": "io.homeassistant.mobile_app", + "app_name": "mobile_app tests", + "app_version": "1.0", + "device_id": "4d5e6f", + "device_name": "Test", + "manufacturer": "Home Assistant", + "model": "mobile_app", + "os_name": "Linux", + "os_version": "5.0.6", + "secret": "123abc", + "supports_encryption": False, + "user_id": "1a2b3c", + "webhook_id": "webhook_id" + }, + domain=DOMAIN, + source="registration", + title="mobile_app test entry", + version=1 + ) + entry.add_to_hass(hass) + + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + + +async def test_notify_works(hass, aioclient_mock, setup_push_receiver): + """Test notify works.""" + assert hass.services.has_service('notify', 'mobile_app_test') is True + assert await hass.services.async_call('notify', 'mobile_app_test', + {'message': 'Hello world'}, + blocking=True) + + assert len(aioclient_mock.mock_calls) == 1 + call = aioclient_mock.mock_calls + + call_json = call[0][2] + + assert call_json["push_token"] == "PUSH_TOKEN" + assert call_json["message"] == "Hello world" + assert call_json["registration_info"]["app_id"] == \ + "io.homeassistant.mobile_app" + assert call_json["registration_info"]["app_version"] == "1.0" From 81a659be0dd4c29f2c30c49307dad87dd542267e Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 04:23:33 +0200 Subject: [PATCH 380/605] Hass.io discovery flow deconz (#22623) * Add Hass.io deCONZ discovery flow * add bridge ID * fix attribute * fix strings * Address comments * Add test * Add only instance / changed maybe later --- .../components/deconz/config_flow.py | 59 +++++++++++++++++-- homeassistant/components/deconz/const.py | 3 + homeassistant/components/deconz/strings.json | 10 +++- homeassistant/components/hassio/discovery.py | 4 +- tests/components/deconz/test_config_flow.py | 49 +++++++++++++++ 5 files changed, 116 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index cabb5b46ece5bc..38849fb37b3ff3 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -1,17 +1,18 @@ """Config flow to configure deCONZ component.""" import asyncio + import async_timeout import voluptuous as vol from homeassistant import config_entries -from homeassistant.core import callback from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT +from homeassistant.core import callback from homeassistant.helpers import aiohttp_client from .const import ( - CONF_ALLOW_DECONZ_GROUPS, CONF_ALLOW_CLIP_SENSOR, DEFAULT_PORT, DOMAIN) - -CONF_BRIDGEID = 'bridgeid' + CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, + DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT, + DOMAIN) @callback @@ -28,6 +29,8 @@ class DeconzFlowHandler(config_entries.ConfigFlow): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + _hassio_discovery = None + def __init__(self): """Initialize the deCONZ config flow.""" self.bridges = [] @@ -151,8 +154,10 @@ async def async_step_options(self, user_input=None): return self.async_show_form( step_id='options', data_schema=vol.Schema({ - vol.Optional(CONF_ALLOW_CLIP_SENSOR): bool, - vol.Optional(CONF_ALLOW_DECONZ_GROUPS): bool, + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, }), ) @@ -191,3 +196,45 @@ async def async_step_import(self, import_config): user_input = {CONF_ALLOW_CLIP_SENSOR: True, CONF_ALLOW_DECONZ_GROUPS: True} return await self.async_step_options(user_input=user_input) + + async def async_step_hassio(self, user_input=None): + """Prepare configuration for a Hass.io deCONZ bridge. + + This flow is triggered by the discovery component. + """ + if configured_hosts(self.hass): + return self.async_abort(reason='one_instance_only') + + self._hassio_discovery = user_input + + return await self.async_step_hassio_confirm() + + async def async_step_hassio_confirm(self, user_input=None): + """Confirm a Hass.io discovery.""" + if user_input is not None: + data = self._hassio_discovery + + return self.async_create_entry( + title=data['addon'], data={ + CONF_HOST: data[CONF_HOST], + CONF_PORT: data[CONF_PORT], + CONF_BRIDGEID: data['serial'], + CONF_API_KEY: data[CONF_API_KEY], + CONF_ALLOW_CLIP_SENSOR: + user_input[CONF_ALLOW_CLIP_SENSOR], + CONF_ALLOW_DECONZ_GROUPS: + user_input[CONF_ALLOW_DECONZ_GROUPS], + }) + + return self.async_show_form( + step_id='hassio_confirm', + description_placeholders={ + 'addon': self._hassio_discovery['addon'] + }, + data_schema=vol.Schema({ + vol.Optional(CONF_ALLOW_CLIP_SENSOR, + default=DEFAULT_ALLOW_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_DECONZ_GROUPS, + default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, + }) + ) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index e728430f202aba..b26fddd914755c 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -6,9 +6,12 @@ DOMAIN = 'deconz' DEFAULT_PORT = 80 +DEFAULT_ALLOW_CLIP_SENSOR = False +DEFAULT_ALLOW_DECONZ_GROUPS = False CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' +CONF_BRIDGEID = 'bridgeid' SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'cover', 'light', 'scene', 'sensor', 'switch'] diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 1bf7235713afbb..d0ae34e7c7aa90 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -19,6 +19,14 @@ "allow_clip_sensor": "Allow importing virtual sensors", "allow_deconz_groups": "Allow importing deCONZ groups" } + }, + "hassio_confirm": { + "title": "deCONZ Zigbee gateway via Hass.io add-on", + "description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the hass.io add-on {addon}?", + "data": { + "allow_clip_sensor": "Allow importing virtual sensors", + "allow_deconz_groups": "Allow importing deCONZ groups" + } } }, "error": { @@ -30,4 +38,4 @@ "one_instance_only": "Component only supports one deCONZ instance" } } -} \ No newline at end of file +} diff --git a/homeassistant/components/hassio/discovery.py b/homeassistant/components/hassio/discovery.py index 804247d2407d82..09a98edc14804b 100644 --- a/homeassistant/components/hassio/discovery.py +++ b/homeassistant/components/hassio/discovery.py @@ -81,7 +81,7 @@ async def async_process_new(self, data): service = data[ATTR_SERVICE] config_data = data[ATTR_CONFIG] - # Read addinional Add-on info + # Read additional Add-on info try: addon_info = await self.hassio.get_addon_info(data[ATTR_ADDON]) except HassioAPIError as err: @@ -98,7 +98,7 @@ async def async_process_del(self, data): service = data[ATTR_SERVICE] uuid = data[ATTR_UUID] - # Check if realy deletet / prevent injections + # Check if really deletet / prevent injections try: data = await self.hassio.get_discovery_message(uuid) except HassioAPIError: diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 20c74a8288310d..863e4e93fc5873 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -265,3 +265,52 @@ async def test_options(hass, aioclient_mock): 'allow_clip_sensor': False, 'allow_deconz_groups': False } + + +async def test_hassio_single_instance(hass): + """Test we only allow a single config flow.""" + MockConfigEntry(domain='deconz', data={ + 'host': '1.2.3.4' + }).add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + 'deconz', context={'source': 'hassio'}) + assert result['type'] == 'abort' + assert result['reason'] == 'one_instance_only' + + +async def test_hassio_confirm(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + 'deconz', + data={ + 'addon': 'Mock Addon', + 'host': 'mock-deconz', + 'port': 8080, + 'serial': 'aa:bb', + 'api_key': '1234567890ABCDEF', + }, + context={'source': 'hassio'} + ) + assert result['type'] == 'form' + assert result['step_id'] == 'hassio_confirm' + assert result['description_placeholders'] == { + 'addon': 'Mock Addon', + } + + result = await hass.config_entries.flow.async_configure( + result['flow_id'], { + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + } + ) + + assert result['type'] == 'create_entry' + assert result['result'].data == { + 'host': 'mock-deconz', + 'port': 8080, + 'bridgeid': 'aa:bb', + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True, + 'allow_deconz_groups': True, + } From e90d980e678156004eac2909f54a5ff3b52080b6 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Tue, 2 Apr 2019 22:57:38 +0200 Subject: [PATCH 381/605] Don't use room setpoint override in climate.opentherm_gw (#22656) * Dont use DATA_ROOM_SETPOINT_OVRD in climate.opentherm_gw as it is unreliable with some thermostats. * Show new target temperature immediately until the backend notices a change * Only update target temp on the gateway if the value differs from the current target_temperature. --- homeassistant/components/opentherm_gw/climate.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 58ce49a9b02bfb..60f1901d43e96d 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -39,6 +39,7 @@ def __init__(self, hass, config): self.temp_precision = config.get(CONF_PRECISION) self._current_operation = STATE_IDLE self._current_temperature = None + self._new_target_temperature = None self._target_temperature = None self._away_mode_a = None self._away_mode_b = None @@ -63,11 +64,10 @@ async def receive_report(self, status): else: self._current_operation = STATE_IDLE self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP) - - temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT_OVRD) - if temp is None: - temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT) - self._target_temperature = temp + temp_upd = status.get(self._gw_vars.DATA_ROOM_SETPOINT) + if self._target_temperature != temp_upd: + self._new_target_temperature = None + self._target_temperature = temp_upd # GPIO mode 5: 0 == Away # GPIO mode 6: 1 == Away @@ -138,7 +138,7 @@ def current_temperature(self): @property def target_temperature(self): """Return the temperature we try to reach.""" - return self._target_temperature + return self._new_target_temperature or self._target_temperature @property def target_temperature_step(self): @@ -154,7 +154,9 @@ async def async_set_temperature(self, **kwargs): """Set new target temperature.""" if ATTR_TEMPERATURE in kwargs: temp = float(kwargs[ATTR_TEMPERATURE]) - self._target_temperature = await self._gateway.set_target_temp( + if temp == self.target_temperature: + return + self._new_target_temperature = await self._gateway.set_target_temp( temp) self.async_schedule_update_ha_state() From 167d8cbaba61c20fe5b8c3338528f47b1e2cf5d0 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Wed, 3 Apr 2019 07:49:53 +0100 Subject: [PATCH 382/605] Fix #22648 - Utility_meter would try to cancel a non existing task (#22669) * don't cancel tariff that are paused * test tariffs --- .../components/utility_meter/sensor.py | 3 +- tests/components/utility_meter/test_sensor.py | 41 +++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index dd1514f5e4351b..2c151634a95adf 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -118,7 +118,8 @@ def async_tariff_change(self, entity, old_state, new_state): self._collecting = async_track_state_change( self.hass, self._sensor_source_id, self.async_reading) else: - self._collecting() + if self._collecting: + self._collecting() self._collecting = None _LOGGER.debug("%s - %s - source <%s>", self._name, diff --git a/tests/components/utility_meter/test_sensor.py b/tests/components/utility_meter/test_sensor.py index ee291439a2c877..6b8705bf776ba3 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -6,10 +6,11 @@ from contextlib import contextmanager from tests.common import async_fire_time_changed -from homeassistant.const import EVENT_HOMEASSISTANT_START +from homeassistant.const import EVENT_HOMEASSISTANT_START, ATTR_ENTITY_ID from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from homeassistant.components.utility_meter.const import DOMAIN +from homeassistant.components.utility_meter.const import ( + DOMAIN, SERVICE_SELECT_TARIFF, ATTR_TARIFF) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN _LOGGER = logging.getLogger(__name__) @@ -31,7 +32,7 @@ async def test_state(hass): 'utility_meter': { 'energy_bill': { 'source': 'sensor.energy', - } + 'tariffs': ['onpeak', 'midpeak', 'offpeak']}, } } @@ -51,11 +52,43 @@ async def test_state(hass): force_update=True) await hass.async_block_till_done() - state = hass.states.get('sensor.energy_bill') + state = hass.states.get('sensor.energy_bill_onpeak') + assert state is not None + assert state.state == '1' + + state = hass.states.get('sensor.energy_bill_midpeak') + assert state is not None + assert state.state == '0' + + state = hass.states.get('sensor.energy_bill_offpeak') assert state is not None + assert state.state == '0' + + await hass.services.async_call(DOMAIN, SERVICE_SELECT_TARIFF, { + ATTR_ENTITY_ID: 'utility_meter.energy_bill', ATTR_TARIFF: 'offpeak' + }, blocking=True) + + await hass.async_block_till_done() + now = dt_util.utcnow() + timedelta(seconds=20) + with patch('homeassistant.util.dt.utcnow', + return_value=now): + hass.states.async_set(entity_id, 6, {"unit_of_measurement": "kWh"}, + force_update=True) + await hass.async_block_till_done() + + state = hass.states.get('sensor.energy_bill_onpeak') + assert state is not None assert state.state == '1' + state = hass.states.get('sensor.energy_bill_midpeak') + assert state is not None + assert state.state == '0' + + state = hass.states.get('sensor.energy_bill_offpeak') + assert state is not None + assert state.state == '3' + async def test_net_consumption(hass): """Test utility sensor state.""" From 9eb4f89da4190d70490c38fb7cd010e4c0f2d223 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 2 Apr 2019 21:23:59 -0700 Subject: [PATCH 383/605] Fix trusted networks auth provider warning message (#22671) * Fix trusted networks auth provider warning message * Update auth.py --- homeassistant/components/http/auth.py | 16 ++++++++++------ homeassistant/components/http/view.py | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 7b8508894ce73d..0d8e327e086e19 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -190,12 +190,16 @@ async def auth_middleware(request, handler): elif (trusted_networks and await async_validate_trusted_networks(request)): - _LOGGER.warning( - 'Access from trusted networks without auth token is going to ' - 'be removed in Home Assistant 0.96. Configure the trusted ' - 'networks auth provider or use long-lived access tokens to ' - 'access %s from %s', - request.path, request[KEY_REAL_IP]) + if request.path not in old_auth_warning: + # When removing this, don't forget to remove the print logic + # in http/view.py + request['deprecate_warning_message'] = \ + 'Access from trusted networks without auth token is ' \ + 'going to be removed in Home Assistant 0.96. Configure ' \ + 'the trusted networks auth provider or use long-lived ' \ + 'access tokens to access {} from {}'.format( + request.path, request[KEY_REAL_IP]) + old_auth_warning.add(request.path) authenticated = True elif (support_legacy and HTTP_HEADER_HA_AUTH in request.headers and diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index d68cabebacf39f..8d5e0ee88b136a 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -98,6 +98,8 @@ async def handle(request): if view.requires_auth: if authenticated: + if 'deprecate_warning_message' in request: + _LOGGER.warning(request['deprecate_warning_message']) await process_success_login(request) else: raise HTTPUnauthorized() From 7cf92c2210c45651e551817c2cd8fbc1c737ec5f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 04:53:44 -0700 Subject: [PATCH 384/605] Deal with cover assumed state (#22673) * Deal with cover assumed state * Add docs --- .../components/google_assistant/trait.py | 17 ++++++++++---- .../components/google_assistant/test_trait.py | 23 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 81918ff2e886ad..de3a9530b5035a 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -27,6 +27,7 @@ TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + ATTR_ASSUMED_STATE, ) from homeassistant.core import DOMAIN as HA_DOMAIN from homeassistant.util import color as color_util, temperature as temp_util @@ -1055,11 +1056,19 @@ def query_attributes(self): response = {} if domain == cover.DOMAIN: - position = self.state.attributes.get(cover.ATTR_CURRENT_POSITION) - if position is not None: - response['openPercent'] = position + # When it's an assumed state, we will always report it as 50% + # Google will not issue an open command if the assumed state is + # open, even if that is currently incorrect. + if self.state.attributes.get(ATTR_ASSUMED_STATE): + response['openPercent'] = 50 else: - if self.state.state != cover.STATE_CLOSED: + position = self.state.attributes.get( + cover.ATTR_CURRENT_POSITION + ) + + if position is not None: + response['openPercent'] = position + elif self.state.state != cover.STATE_CLOSED: response['openPercent'] = 100 else: response['openPercent'] = 0 diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index a0a710d3d8ce27..81a7fbe1bf7a4f 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -21,7 +21,8 @@ from homeassistant.components.google_assistant import trait, helpers, const from homeassistant.const import ( STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, - TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE) + TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + ATTR_ASSUMED_STATE) from homeassistant.core import State, DOMAIN as HA_DOMAIN, EVENT_CALL_SERVICE from homeassistant.util import color from tests.common import async_mock_service, mock_coro @@ -1059,12 +1060,30 @@ async def test_openclose_cover(hass): assert trait.OpenCloseTrait.supported(cover.DOMAIN, cover.SUPPORT_SET_POSITION) + # No position trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { - cover.ATTR_CURRENT_POSITION: 75 }), BASIC_CONFIG) assert trt.sync_attributes() == {} + assert trt.query_attributes() == { + 'openPercent': 100 + } + + # Assumed state + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + ATTR_ASSUMED_STATE: True, + }), BASIC_CONFIG) + assert trt.sync_attributes() == {} + assert trt.query_attributes() == { + 'openPercent': 50 + } + + trt = trait.OpenCloseTrait(hass, State('cover.bla', cover.STATE_OPEN, { + cover.ATTR_CURRENT_POSITION: 75 + }), BASIC_CONFIG) + + assert trt.sync_attributes() == {} assert trt.query_attributes() == { 'openPercent': 75 } From 836aab283fe683e9d0b0520e01135678683939aa Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 3 Apr 2019 13:46:41 +0200 Subject: [PATCH 385/605] Fix ffmpeg default extra options (#22682) --- homeassistant/components/amcrest/__init__.py | 4 +++- homeassistant/components/arlo/camera.py | 7 ++++--- homeassistant/components/canary/camera.py | 7 ++++--- homeassistant/components/ffmpeg/camera.py | 4 +++- homeassistant/components/onvif/camera.py | 2 +- homeassistant/components/xiaomi/camera.py | 3 ++- homeassistant/components/yi/camera.py | 3 ++- 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index 84dc0b5bb014e0..9f43941505b68c 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -26,6 +26,7 @@ DEFAULT_PORT = 80 DEFAULT_RESOLUTION = 'high' DEFAULT_STREAM_SOURCE = 'snapshot' +DEFAULT_ARGUMENTS = '-pred 1' TIMEOUT = 10 DATA_AMCREST = 'amcrest' @@ -77,7 +78,8 @@ vol.All(vol.In(RESOLUTION_LIST)), vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE): vol.All(vol.In(STREAM_SOURCE_LIST)), - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): + cv.string, vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): cv.time_period, vol.Optional(CONF_SENSORS): diff --git a/homeassistant/components/arlo/camera.py b/homeassistant/components/arlo/camera.py index 95d11318bf7cbf..d4b00f0062503a 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -13,6 +13,8 @@ from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO +DEPENDENCIES = ['arlo', 'ffmpeg'] + _LOGGER = logging.getLogger(__name__) ARLO_MODE_ARMED = 'armed' @@ -28,8 +30,7 @@ ATTR_LAST_REFRESH = 'last_refresh' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' - -DEPENDENCIES = ['arlo', 'ffmpeg'] +DEFAULT_ARGUMENTS = '-pred 1' POWERSAVE_MODE_MAPPING = { 1: 'best_battery_life', @@ -38,7 +39,7 @@ } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, }) diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index 63c27d31d9339c..c3a3af32450e38 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -18,16 +18,17 @@ from . import DATA_CANARY, DEFAULT_TIMEOUT -CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' - DEPENDENCIES = ['canary', 'ffmpeg'] _LOGGER = logging.getLogger(__name__) +CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' +DEFAULT_ARGUMENTS = '-pred 1' + MIN_TIME_BETWEEN_SESSION_RENEW = timedelta(seconds=90) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, }) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index d897293124bee6..07e56cfd51fdf7 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -20,11 +20,13 @@ _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['ffmpeg'] + DEFAULT_NAME = 'FFmpeg' +DEFAULT_ARGUMENTS = "-pred 1" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_INPUT): cv.string, - vol.Optional(CONF_EXTRA_ARGUMENTS): cv.string, + vol.Optional(CONF_EXTRA_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 09d47c3c0c9761..a196f87cd10f2e 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -33,7 +33,7 @@ DEFAULT_PORT = 5000 DEFAULT_USERNAME = 'admin' DEFAULT_PASSWORD = '888888' -DEFAULT_ARGUMENTS = '-q:v 2' +DEFAULT_ARGUMENTS = '-pred 1' DEFAULT_PROFILE = 0 CONF_PROFILE = "profile" diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index d8cd59129abb59..b0acf50ec8cd46 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -23,6 +23,7 @@ DEFAULT_PATH = '/media/mmcblk0p1/record' DEFAULT_PORT = 21 DEFAULT_USERNAME = 'root' +DEFAULT_ARGUMENTS = '-pred 1' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' CONF_MODEL = 'model' @@ -39,7 +40,7 @@ vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string }) diff --git a/homeassistant/components/yi/camera.py b/homeassistant/components/yi/camera.py index c60d4971fb843c..f82c8c38129cb7 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -26,6 +26,7 @@ DEFAULT_PATH = '/tmp/sd/record' DEFAULT_PORT = 21 DEFAULT_USERNAME = 'root' +DEFAULT_ARGUMENTS = '-pred 1' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' @@ -36,7 +37,7 @@ vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string, vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string }) From 2e8c69003315ce6d9c463be8150aead9c2b97a96 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 09:54:49 -0700 Subject: [PATCH 386/605] A very basic Circleci setup (#22503) * Add circleci support * Add buildpack-deps * Install libudev-dev * sudo * always run test * Add test report * no sugar * quite pytest * better junit test result * Add $CODE_COVERAGE env var --- .circleci/config.yml | 79 ++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 80 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000000..b6a57a283815cb --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,79 @@ +# Python CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-python/ for more details +# +version: 2.1 +jobs: + build: + docker: + # specify the version you desire here + # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` + - image: circleci/python:3.7.2 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/postgres:9.4 + - image: circleci/buildpack-deps:stretch + + working_directory: ~/repo + + steps: + - checkout + + - run: + name: setup docker prereqs + command: sudo apt-get update && sudo apt-get install -y --no-install-recommends libudev-dev + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "requirements_all.txt" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: + name: install dependencies + command: | + python3 -m venv venv + . venv/bin/activate + pip install --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt + + - save_cache: + paths: + - ./venv + key: v1-dependencies-{{ checksum "requirements_all.txt" }} + + - run: + name: install + command: | + . venv/bin/activate + pip install --progress-bar off -e . + + - run: + name: run lint + command: | + . venv/bin/activate + python script/gen_requirements_all.py validate + flake8 + pylint homeassistant + + - run: + name: run tests + command: | + . venv/bin/activate + if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi + pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH + script/check_dirty + when: always + + - store_test_results: + path: test-reports + + - store_artifacts: + path: htmlcov + destination: cov-reports + + - store_artifacts: + path: test-reports + destination: test-reports \ No newline at end of file diff --git a/.gitignore b/.gitignore index 91b8d024aedd02..b486032c741429 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ pip-log.txt .tox nosetests.xml htmlcov/ +test-reports/ # Translations *.mo From 273007fa190d4dc5affc0b2d093806e15296615f Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 28 Mar 2019 14:37:10 -0700 Subject: [PATCH 387/605] Fix Circleci config (#22509) * Add libav depends on circleci * tweak circleci config --- .circleci/config.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b6a57a283815cb..112ce2284dde58 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,26 +23,26 @@ jobs: - run: name: setup docker prereqs - command: sudo apt-get update && sudo apt-get install -y --no-install-recommends libudev-dev + command: sudo apt-get update && sudo apt-get install -y --no-install-recommends + libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev + libswscale-dev libswresample-dev libavfilter-dev - # Download and cache dependencies + # Download and cache dependencies, we don't use fallback cache - restore_cache: keys: - - v1-dependencies-{{ checksum "requirements_all.txt" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- + - v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - run: name: install dependencies command: | python3 -m venv venv . venv/bin/activate - pip install --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt + pip install -q --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt - save_cache: paths: - ./venv - key: v1-dependencies-{{ checksum "requirements_all.txt" }} + key: v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - run: name: install @@ -76,4 +76,4 @@ jobs: - store_artifacts: path: test-reports - destination: test-reports \ No newline at end of file + destination: test-reports From 5dd444fcd81c6a4be70d85f9487d9c0381867cc2 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 29 Mar 2019 16:37:45 -0700 Subject: [PATCH 388/605] Set up Circleci workflow (#22519) * Set up Circleci workflow * Update python tag * Add pre-test job to cache the requirements * Upgrade pip itself * Use 3.7 for lint * Parallelize pylint * Tweak run gen_requirements_all * tweak cache key --- .circleci/config.yml | 201 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 173 insertions(+), 28 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 112ce2284dde58..f9eb28bdf4aa86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,67 +3,174 @@ # Check https://circleci.com/docs/2.0/language-python/ for more details # version: 2.1 -jobs: - build: + +executors: + + python: + parameters: + tag: + type: string + default: latest docker: - # specify the version you desire here - # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` - - image: circleci/python:3.7.2 - - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # - image: circleci/postgres:9.4 + - image: circleci/python:<< parameters.tag >> - image: circleci/buildpack-deps:stretch - working_directory: ~/repo - steps: - - checkout +commands: - - run: - name: setup docker prereqs - command: sudo apt-get update && sudo apt-get install -y --no-install-recommends - libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev - libswscale-dev libswresample-dev libavfilter-dev + docker-prereqs: + description: Set up docker prerequisite requirement + steps: + - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends + libudev-dev libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev + libswscale-dev libswresample-dev libavfilter-dev - # Download and cache dependencies, we don't use fallback cache + install-requirements: + description: Set up venv and install requirements python packages with cache support + parameters: + python: + type: string + default: latest + all: + description: pip install -r requirements_all.txt + type: boolean + default: false + test: + description: pip install -r requirements_test.txt + type: boolean + default: false + test_all: + description: pip install -r requirements_test_all.txt + type: boolean + default: false + steps: - restore_cache: keys: - - v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} - + - v1-<< parameters.python >>-{{ checksum "homeassistant/package_constraints.txt" }}-<<# parameters.all >>{{ checksum "requirements_all.txt" }}<>-<<# parameters.test >>{{ checksum "requirements_test.txt" }}<>-<<# parameters.test_all >>{{ checksum "requirements_test_all.txt" }}<> - run: name: install dependencies command: | python3 -m venv venv . venv/bin/activate - pip install -q --progress-bar off -r requirements_all.txt -r requirements_test.txt -c homeassistant/package_constraints.txt - + pip install -U pip + <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> + <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> + <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> - save_cache: paths: - ./venv - key: v1-dependencies-{{ checksum "requirements_all.txt" }}-{{ checksum "requirements_test.txt" }} + key: v1-<< parameters.python >>-{{ checksum "homeassistant/package_constraints.txt" }}-<<# parameters.all >>{{ checksum "requirements_all.txt" }}<>-<<# parameters.test >>{{ checksum "requirements_test.txt" }}<>-<<# parameters.test_all >>{{ checksum "requirements_test_all.txt" }}<> + install: + description: Install Home Assistant + steps: - run: name: install command: | . venv/bin/activate pip install --progress-bar off -e . +jobs: + + static-check: + executor: + name: python + tag: 3.7-stretch + + steps: + - checkout + - docker-prereqs + - run: - name: run lint + name: run static check command: | + python3 -m venv venv . venv/bin/activate - python script/gen_requirements_all.py validate + pip install -U pip + pip install --progress-bar off flake8 flake8 - pylint homeassistant + + - install + - run: + name: run gen_requirements_all + command: | + . venv/bin/activate + python script/gen_requirements_all.py validate + + pre-install-all-requirements: + executor: + name: python + tag: 3.7-stretch + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: 3.7-stretch + all: true + test: true + + pylint: + executor: + name: python + tag: 3.7-stretch + parallelism: 3 + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: 3.7-stretch + all: true + test: true + - install + + - run: + name: run pylint + command: | + . venv/bin/activate + PYFILES=$(circleci tests glob "homeassistant/**/*.py" | circleci tests split) + pylint ${PYFILES} + + pre-test: + parameters: + python: + type: string + executor: + name: python + tag: << parameters.python >> + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: << parameters.python >> + test_all: true + + test: + parameters: + python: + type: string + executor: + name: python + tag: << parameters.python >> + parallelism: 3 + + steps: + - checkout + - docker-prereqs + - install-requirements: + python: << parameters.python >> + test_all: true + - install - run: name: run tests command: | . venv/bin/activate + TESTFILES=$(circleci tests glob "tests/**/test_*.py" | circleci tests split --split-by=timings) if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi - pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH + pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty when: always @@ -77,3 +184,41 @@ jobs: - store_artifacts: path: test-reports destination: test-reports + +workflows: + version: 2 + build: + jobs: + - static-check + - pre-install-all-requirements + - pylint: + requires: + - pre-install-all-requirements + - pre-test: + name: pre-test 3.5.5 + python: 3.5.5-stretch + - pre-test: + name: pre-test 3.6 + python: 3.6-stretch + - pre-test: + name: pre-test 3.7 + python: 3.7-stretch + - test: + name: test 3.5.5 + requires: + - pre-test 3.5.5 + python: 3.5.5-stretch + - test: + name: test 3.6 + requires: + - pre-test 3.6 + python: 3.6-stretch + - test: + name: test 3.7 + requires: + - pre-test 3.7 + python: 3.7-stretch + # CircleCI does not allow failure yet + # - test: + # name: test 3.8 + # python: 3.8-rc-stretch From 2c105632050bbe31973faf9c930aac60d413194b Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 07:12:59 -0700 Subject: [PATCH 389/605] Config CircleCI workflow (#22590) * Add mypyrc to control typing check, add mypy to circle * Add translation upload circlci job --- .circleci/config.yml | 47 ++++++++++++++++++++++++++++++-------- README.rst | 6 +++-- mypyrc | 21 +++++++++++++++++ script/translations_upload | 3 ++- tox.ini | 2 +- 5 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 mypyrc diff --git a/.circleci/config.yml b/.circleci/config.yml index f9eb28bdf4aa86..b4f22601bb5c8a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ commands: command: | python3 -m venv venv . venv/bin/activate - pip install -U pip + pip install -q -U pip <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> @@ -68,28 +68,35 @@ commands: name: install command: | . venv/bin/activate - pip install --progress-bar off -e . + pip install -q --progress-bar off -e . jobs: static-check: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch steps: - checkout - docker-prereqs + - install-requirements: + python: 3.5.5-stretch + test: true - run: name: run static check command: | - python3 -m venv venv . venv/bin/activate - pip install -U pip - pip install --progress-bar off flake8 flake8 + - run: + name: run static type check + command: | + . venv/bin/activate + TYPING_FILES=$(cat mypyrc) + mypy $TYPING_FILES + - install - run: name: run gen_requirements_all @@ -114,7 +121,7 @@ jobs: executor: name: python tag: 3.7-stretch - parallelism: 3 + parallelism: 2 steps: - checkout @@ -154,7 +161,7 @@ jobs: executor: name: python tag: << parameters.python >> - parallelism: 3 + parallelism: 2 steps: - checkout @@ -172,7 +179,6 @@ jobs: if [ -z "$CODE_COVERAGE" ]; then CC_SWITCH=""; else CC_SWITCH="--cov --cov-report html:htmlcov"; fi pytest --timeout=9 --duration=10 --junitxml=test-reports/homeassistant/results.xml -qq -o junit_family=xunit2 -o junit_suite_name=homeassistant -o console_output_style=count -p no:sugar $CC_SWITCH -- ${TESTFILES} script/check_dirty - when: always - store_test_results: path: test-reports @@ -185,6 +191,23 @@ jobs: path: test-reports destination: test-reports + # This job use machine executor, e.g. classic CircleCI VM because we need both lokalise-cli and a Python runtime. + # Classic CircleCI included python 2.7.12 and python 3.5.2 managed by pyenv, the Python version may need change if + # CircleCI changed its VM in future. + upload-translations: + machine: true + + steps: + - checkout + + - run: + name: upload english translations + command: | + pyenv versions + pyenv global 3.5.2 + docker pull lokalise/lokalise-cli@sha256:2198814ebddfda56ee041a4b427521757dd57f75415ea9693696a64c550cef21 + script/translations_upload + workflows: version: 2 build: @@ -222,3 +245,9 @@ workflows: # - test: # name: test 3.8 # python: 3.8-rc-stretch + - upload-translations: + requires: + - static-check + filters: + branches: + only: dev diff --git a/README.rst b/README.rst index f231d6c5514c94..941a463fb37410 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -Home Assistant |Build Status| |Coverage Status| |Chat Status| +Home Assistant |Build Status| |CI Status| |Coverage Status| |Chat Status| ================================================================================= Home Assistant is a home automation platform running on Python 3. It is able to track and control all devices at home and offer a platform for automating control. @@ -27,8 +27,10 @@ components `__ of our website for further help and information. -.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master +.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=dev :target: https://travis-ci.org/home-assistant/home-assistant +.. |CI Status| image:: https://circleci.com/gh/home-assistant/home-assistant.svg?style=shield + :target: https://circleci.com/gh/home-assistant/home-assistant .. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg :target: https://coveralls.io/r/home-assistant/home-assistant?branch=master .. |Chat Status| image:: https://img.shields.io/discord/330944238910963714.svg diff --git a/mypyrc b/mypyrc new file mode 100644 index 00000000000000..7c73d12e3815a0 --- /dev/null +++ b/mypyrc @@ -0,0 +1,21 @@ +homeassistant/*.py +homeassistant/auth/ +homeassistant/util/ +homeassistant/helpers/__init__.py +homeassistant/helpers/aiohttp_client.py +homeassistant/helpers/area_registry.py +homeassistant/helpers/condition.py +homeassistant/helpers/deprecation.py +homeassistant/helpers/dispatcher.py +homeassistant/helpers/entity_values.py +homeassistant/helpers/entityfilter.py +homeassistant/helpers/icon.py +homeassistant/helpers/intent.py +homeassistant/helpers/json.py +homeassistant/helpers/location.py +homeassistant/helpers/signal.py +homeassistant/helpers/state.py +homeassistant/helpers/sun.py +homeassistant/helpers/temperature.py +homeassistant/helpers/translation.py +homeassistant/helpers/typing.py diff --git a/script/translations_upload b/script/translations_upload index 5bf9fe1e1217cb..52045e41d60c81 100755 --- a/script/translations_upload +++ b/script/translations_upload @@ -26,7 +26,8 @@ LANG_ISO=en CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) -if [ "${CURRENT_BRANCH-}" != "dev" ] && [ "${TRAVIS_BRANCH-}" != "dev" ] ; then +# Check Travis and CircleCI environment as well +if [ "${CURRENT_BRANCH-}" != "dev" ] && [ "${TRAVIS_BRANCH-}" != "dev" ] && [ "${CIRCLE_BRANCH-}" != "dev" ]; then echo "Please only run the translations upload script from a clean checkout of dev." exit 1 fi diff --git a/tox.ini b/tox.ini index 8423141df60819..b8995d9e877b94 100644 --- a/tox.ini +++ b/tox.ini @@ -42,4 +42,4 @@ deps = -r{toxinidir}/requirements_test.txt -c{toxinidir}/homeassistant/package_constraints.txt commands = - /bin/bash -c 'mypy homeassistant/*.py homeassistant/{auth,util}/ homeassistant/helpers/{__init__,aiohttp_client,area_registry,condition,deprecation,dispatcher,entity_values,entityfilter,icon,intent,json,location,signal,state,sun,temperature,translation,typing}.py' + /bin/bash -c 'TYPING_FILES=$(cat mypyrc); mypy $TYPING_FILES' From b30c1406484641c8ed773c0ac762197f8dc8b514 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Mon, 1 Apr 2019 17:42:04 -0700 Subject: [PATCH 390/605] Require static-check success first for rest of workflow (#22635) * Require static-check success first * Update config.yml --- .circleci/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b4f22601bb5c8a..9c9d75d934bd54 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -213,18 +213,26 @@ workflows: build: jobs: - static-check - - pre-install-all-requirements + - pre-install-all-requirements: + requires: + - static-check - pylint: requires: - pre-install-all-requirements - pre-test: name: pre-test 3.5.5 + requires: + - static-check python: 3.5.5-stretch - pre-test: name: pre-test 3.6 + requires: + - static-check python: 3.6-stretch - pre-test: name: pre-test 3.7 + requires: + - static-check python: 3.7-stretch - test: name: test 3.5.5 From 685de23a4e36f8d5c3234071c826f9a3115d47de Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 1 Apr 2019 21:51:43 -0700 Subject: [PATCH 391/605] Run PyLint under Python 3.5 (#22642) * Run PyLint under Python 3.5 * Remove -q from pip install to debug * Upgrade setuptools before install * Use correct cache key for pylint --- .circleci/config.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c9d75d934bd54..c8d7c7ee6b2602 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: tag: type: string - default: latest + default: latest docker: - image: circleci/python:<< parameters.tag >> - image: circleci/buildpack-deps:stretch @@ -53,6 +53,7 @@ commands: python3 -m venv venv . venv/bin/activate pip install -q -U pip + pip install -q -U setuptools <<# parameters.all >>pip install -q --progress-bar off -r requirements_all.txt -c homeassistant/package_constraints.txt<> <<# parameters.test >>pip install -q --progress-bar off -r requirements_test.txt -c homeassistant/package_constraints.txt<> <<# parameters.test_all >>pip install -q --progress-bar off -r requirements_test_all.txt -c homeassistant/package_constraints.txt<> @@ -107,27 +108,27 @@ jobs: pre-install-all-requirements: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch steps: - checkout - docker-prereqs - install-requirements: - python: 3.7-stretch + python: 3.5.5-stretch all: true test: true pylint: executor: name: python - tag: 3.7-stretch + tag: 3.5.5-stretch parallelism: 2 steps: - checkout - docker-prereqs - install-requirements: - python: 3.7-stretch + python: 3.5.5-stretch all: true test: true - install From 63d8dd9f7a4058526c31605084fdce2e9b83d89a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 11:27:57 -0700 Subject: [PATCH 392/605] Bumped version to 0.91.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4091bc2ed02e02..36c61a51916258 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0b5' +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 98644135fac42cf7ee56e96fd88b53a923580c4d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 3 Apr 2019 20:30:03 +0200 Subject: [PATCH 393/605] Update light/services.yaml (#22662) --- homeassistant/components/light/services.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index cdf82e97429ad2..d4985258368b8a 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -31,10 +31,10 @@ turn_on: description: Number between 0..255 indicating level of white. example: '250' brightness: - description: Number between 0..255 indicating brightness. + description: Number between 0..255 indicating brightness, where 0 turns the light off, 1 is the minimum brightness and 255 is the maximum brightness supported by the light. example: 120 brightness_pct: - description: Number between 0..100 indicating percentage of full brightness. + description: Number between 0..100 indicating percentage of full brightness, where 0 turns the light off, 1 is the minimum brightness and 100 is the maximum brightness supported by the light. example: 47 profile: description: Name of a light profile to use. From a85bcce857e02b6e202232df481b633be06d5119 Mon Sep 17 00:00:00 2001 From: ehendrix23 Date: Wed, 3 Apr 2019 18:29:49 -0600 Subject: [PATCH 394/605] Fix connection loss issues for Harmony (#22687) * Increase aioharmony version to 0.1.11 Update aioharmony version to 0.1.11, this new update contains fixes for websocket connection losses. * Update requirements_all --- homeassistant/components/harmony/remote.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/harmony/remote.py b/homeassistant/components/harmony/remote.py index 78f2674243cffb..12b3a91e12b675 100644 --- a/homeassistant/components/harmony/remote.py +++ b/homeassistant/components/harmony/remote.py @@ -16,7 +16,7 @@ from homeassistant.exceptions import PlatformNotReady from homeassistant.util import slugify -REQUIREMENTS = ['aioharmony==0.1.8'] +REQUIREMENTS = ['aioharmony==0.1.11'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 8ed77fdfb3e89a..801f850375dc2f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -121,7 +121,7 @@ aiofreepybox==0.0.8 aioftp==0.12.0 # homeassistant.components.harmony.remote -aioharmony==0.1.8 +aioharmony==0.1.11 # homeassistant.components.emulated_hue # homeassistant.components.http From 6aac49de7ef3fad33aa6c7878b2ea7c51b241fda Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 3 Apr 2019 20:14:02 -0700 Subject: [PATCH 395/605] Remove aws_* notify platforms (#22698) --- .coveragerc | 3 - CODEOWNERS | 30 +++---- .../components/aws_lambda/__init__.py | 1 - homeassistant/components/aws_lambda/notify.py | 89 ------------------- homeassistant/components/aws_sns/__init__.py | 1 - homeassistant/components/aws_sns/notify.py | 79 ---------------- homeassistant/components/aws_sqs/__init__.py | 1 - homeassistant/components/aws_sqs/notify.py | 81 ----------------- requirements_all.txt | 3 - 9 files changed, 14 insertions(+), 274 deletions(-) delete mode 100644 homeassistant/components/aws_lambda/__init__.py delete mode 100644 homeassistant/components/aws_lambda/notify.py delete mode 100644 homeassistant/components/aws_sns/__init__.py delete mode 100644 homeassistant/components/aws_sns/notify.py delete mode 100644 homeassistant/components/aws_sqs/__init__.py delete mode 100644 homeassistant/components/aws_sqs/notify.py diff --git a/.coveragerc b/.coveragerc index 25e5cf8bb037a5..f8d8b0fc5215ef 100644 --- a/.coveragerc +++ b/.coveragerc @@ -47,9 +47,6 @@ omit = homeassistant/components/august/* homeassistant/components/automatic/device_tracker.py homeassistant/components/avion/light.py - homeassistant/components/aws_lambda/notify.py - homeassistant/components/aws_sns/notify.py - homeassistant/components/aws_sqs/notify.py homeassistant/components/baidu/tts.py homeassistant/components/bbb_gpio/* homeassistant/components/bbox/device_tracker.py diff --git a/CODEOWNERS b/CODEOWNERS index 9abf1396c6159e..276c730a47a19a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -47,31 +47,18 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave homeassistant/components/hassio/* @home-assistant/hassio # Individual platforms -homeassistant/components/notify/aws_lambda.py @robbiet480 -homeassistant/components/notify/aws_sns.py @robbiet480 -homeassistant/components/notify/aws_sqs.py @robbiet480 -homeassistant/components/notify/file.py @fabaff -homeassistant/components/notify/flock.py @fabaff -homeassistant/components/notify/gntp.py @robbiet480 -homeassistant/components/notify/html5.py @robbiet480 -homeassistant/components/notify/mastodon.py @fabaff -homeassistant/components/notify/smtp.py @fabaff -homeassistant/components/notify/syslog.py @fabaff -homeassistant/components/notify/twilio_call.py @robbiet480 -homeassistant/components/notify/twilio_sms.py @robbiet480 -homeassistant/components/notify/xmpp.py @fabaff -homeassistant/components/notify/yessssms.py @flowolf -homeassistant/components/tts/amazon_polly.py @robbiet480 # A homeassistant/components/airvisual/sensor.py @bachya homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell homeassistant/components/alpha_vantage/sensor.py @fabaff +homeassistant/components/amazon_polly/* @robbiet480 homeassistant/components/ambient_station/* @bachya homeassistant/components/arduino/* @fabaff homeassistant/components/arest/* @fabaff homeassistant/components/asuswrt/device_tracker.py @kennedyshead homeassistant/components/automatic/device_tracker.py @armills +homeassistant/components/aws/* @awarecan @robbiet480 homeassistant/components/axis/* @kane610 # B @@ -112,10 +99,11 @@ homeassistant/components/eq3btsmart/climate.py @rytilahti homeassistant/components/esphome/* @OttoWinter # F -homeassistant/components/file/sensor.py @fabaff +homeassistant/components/file/* @fabaff homeassistant/components/filter/sensor.py @dgomes homeassistant/components/fitbit/sensor.py @robbiet480 homeassistant/components/fixer/sensor.py @fabaff +homeassistant/components/flock/notify.py @fabaff homeassistant/components/flunearyou/sensor.py @bachya homeassistant/components/foursquare/* @robbiet480 homeassistant/components/freebox/* @snoof85 @@ -124,6 +112,7 @@ homeassistant/components/freebox/* @snoof85 homeassistant/components/gearbest/sensor.py @HerrHofrat homeassistant/components/gitter/sensor.py @fabaff homeassistant/components/glances/sensor.py @fabaff +homeassistant/components/gntp/notify.py @robbiet480 homeassistant/components/google_travel_time/sensor.py @robbiet480 homeassistant/components/googlehome/* @ludeeus homeassistant/components/gpsd/sensor.py @fabaff @@ -136,6 +125,7 @@ homeassistant/components/hikvision/binary_sensor.py @mezz64 homeassistant/components/history_graph/* @andrey-git homeassistant/components/hive/* @Rendili @KJonline homeassistant/components/homekit/* @cdce8p +homeassistant/components/html5/notify.py @robbiet480 homeassistant/components/huawei_lte/* @scop homeassistant/components/huawei_router/device_tracker.py @abmantis @@ -165,6 +155,7 @@ homeassistant/components/liveboxplaytv/media_player.py @pschmitt homeassistant/components/luftdaten/* @fabaff # M +homeassistant/components/mastodon/notify.py @fabaff homeassistant/components/matrix/* @tinloaf homeassistant/components/mediaroom/media_player.py @dgomes homeassistant/components/melissa/* @kennedyshead @@ -181,6 +172,7 @@ homeassistant/components/mystrom/* @fabaff # N homeassistant/components/nello/lock.py @pschmitt homeassistant/components/ness_alarm/* @nickw444 +homeassistant/components/nest/* @awarecan homeassistant/components/netdata/sensor.py @fabaff homeassistant/components/nissan_leaf/* @filcole homeassistant/components/nmbs/sensor.py @thibmaek @@ -225,6 +217,7 @@ homeassistant/components/shodan/sensor.py @fabaff homeassistant/components/simplisafe/* @bachya homeassistant/components/sma/sensor.py @kellerza homeassistant/components/smartthings/* @andrewsayre +homeassistant/components/smtp/notify.py @fabaff homeassistant/components/sonos/* @amelchio homeassistant/components/spaceapi/* @fabaff homeassistant/components/spider/* @peternijssen @@ -234,6 +227,7 @@ homeassistant/components/swiss_*/* @fabaff homeassistant/components/switchbot/switch.py @danielhiversen homeassistant/components/switchmate/switch.py @danielhiversen homeassistant/components/synology_srm/device_tracker.py @aerialls +homeassistant/components/syslog/notify.py @fabaff homeassistant/components/sytadin/sensor.py @gautric # T @@ -252,6 +246,8 @@ homeassistant/components/toon/* @frenck homeassistant/components/tplink/* @rytilahti homeassistant/components/traccar/device_tracker.py @ludeeus homeassistant/components/tradfri/* @ggravlingen +homeassistant/components/twilio_call/notify.py @robbiet480 +homeassistant/components/twilio_sms/notify.py @robbiet480 # U homeassistant/components/uber/sensor.py @robbiet480 @@ -276,11 +272,13 @@ homeassistant/components/xfinity/device_tracker.py @cisasteelersfan homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi homeassistant/components/xiaomi_miio/* @rytilahti @syssi homeassistant/components/xiaomi_tv/media_player.py @fattdev +homeassistant/components/xmpp/notify.py @fabaff # Y homeassistant/components/yamaha_musiccast/* @jalmeroth homeassistant/components/yeelight/* @rytilahti @zewelor homeassistant/components/yeelightsunflower/light.py @lindsaymarkward +homeassistant/components/yessssms/notify.py @flowolf homeassistant/components/yi/camera.py @bachya # Z diff --git a/homeassistant/components/aws_lambda/__init__.py b/homeassistant/components/aws_lambda/__init__.py deleted file mode 100644 index f6d86d02e93f31..00000000000000 --- a/homeassistant/components/aws_lambda/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The aws_lambda component.""" diff --git a/homeassistant/components/aws_lambda/notify.py b/homeassistant/components/aws_lambda/notify.py deleted file mode 100644 index e5fed20d9976c5..00000000000000 --- a/homeassistant/components/aws_lambda/notify.py +++ /dev/null @@ -1,89 +0,0 @@ -"""AWS Lambda platform for notify component.""" -import base64 -import json -import logging - -import voluptuous as vol - -from homeassistant.const import CONF_NAME, CONF_PLATFORM -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.json import JSONEncoder - -from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, - BaseNotificationService) - -REQUIREMENTS = ['boto3==1.9.16'] - -_LOGGER = logging.getLogger(__name__) - -CONF_REGION = 'region_name' -CONF_ACCESS_KEY_ID = 'aws_access_key_id' -CONF_SECRET_ACCESS_KEY = 'aws_secret_access_key' -CONF_PROFILE_NAME = 'profile_name' -CONF_CONTEXT = 'context' -ATTR_CREDENTIALS = 'credentials' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_REGION, default='us-east-1'): cv.string, - vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, - vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, - vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, - vol.Optional(CONF_CONTEXT, default=dict()): vol.Coerce(dict) -}) - - -def get_service(hass, config, discovery_info=None): - """Get the AWS Lambda notification service.""" - _LOGGER.warning( - "aws_lambda notify platform is deprecated, please replace it" - " with aws component. This config will become invalid in version 0.92." - " See https://www.home-assistant.io/components/aws/ for details." - ) - - context_str = json.dumps({'custom': config[CONF_CONTEXT]}, cls=JSONEncoder) - context_b64 = base64.b64encode(context_str.encode('utf-8')) - context = context_b64.decode('utf-8') - - import boto3 - - aws_config = config.copy() - - del aws_config[CONF_PLATFORM] - del aws_config[CONF_NAME] - del aws_config[CONF_CONTEXT] - - profile = aws_config.get(CONF_PROFILE_NAME) - - if profile is not None: - boto3.setup_default_session(profile_name=profile) - del aws_config[CONF_PROFILE_NAME] - - lambda_client = boto3.client("lambda", **aws_config) - - return AWSLambda(lambda_client, context) - - -class AWSLambda(BaseNotificationService): - """Implement the notification service for the AWS Lambda service.""" - - def __init__(self, lambda_client, context): - """Initialize the service.""" - self.client = lambda_client - self.context = context - - def send_message(self, message="", **kwargs): - """Send notification to specified LAMBDA ARN.""" - targets = kwargs.get(ATTR_TARGET) - - if not targets: - _LOGGER.info("At least 1 target is required") - return - - for target in targets: - cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) - payload = {"message": message} - payload.update(cleaned_kwargs) - - self.client.invoke(FunctionName=target, - Payload=json.dumps(payload), - ClientContext=self.context) diff --git a/homeassistant/components/aws_sns/__init__.py b/homeassistant/components/aws_sns/__init__.py deleted file mode 100644 index b51698ce0dcaab..00000000000000 --- a/homeassistant/components/aws_sns/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The aws_sns component.""" diff --git a/homeassistant/components/aws_sns/notify.py b/homeassistant/components/aws_sns/notify.py deleted file mode 100644 index daac710d40ac4d..00000000000000 --- a/homeassistant/components/aws_sns/notify.py +++ /dev/null @@ -1,79 +0,0 @@ -"""AWS SNS platform for notify component.""" -import json -import logging - -import voluptuous as vol - -from homeassistant.const import CONF_NAME, CONF_PLATFORM -import homeassistant.helpers.config_validation as cv - -from homeassistant.components.notify import ( - ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, - BaseNotificationService) - -_LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["boto3==1.9.16"] - -CONF_REGION = 'region_name' -CONF_ACCESS_KEY_ID = 'aws_access_key_id' -CONF_SECRET_ACCESS_KEY = 'aws_secret_access_key' -CONF_PROFILE_NAME = 'profile_name' -ATTR_CREDENTIALS = 'credentials' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_REGION, default='us-east-1'): cv.string, - vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, - vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, - vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, -}) - - -def get_service(hass, config, discovery_info=None): - """Get the AWS SNS notification service.""" - _LOGGER.warning( - "aws_sns notify platform is deprecated, please replace it" - " with aws component. This config will become invalid in version 0.92." - " See https://www.home-assistant.io/components/aws/ for details." - ) - - import boto3 - - aws_config = config.copy() - - del aws_config[CONF_PLATFORM] - del aws_config[CONF_NAME] - - profile = aws_config.get(CONF_PROFILE_NAME) - - if profile is not None: - boto3.setup_default_session(profile_name=profile) - del aws_config[CONF_PROFILE_NAME] - - sns_client = boto3.client("sns", **aws_config) - - return AWSSNS(sns_client) - - -class AWSSNS(BaseNotificationService): - """Implement the notification service for the AWS SNS service.""" - - def __init__(self, sns_client): - """Initialize the service.""" - self.client = sns_client - - def send_message(self, message="", **kwargs): - """Send notification to specified SNS ARN.""" - targets = kwargs.get(ATTR_TARGET) - - if not targets: - _LOGGER.info("At least 1 target is required") - return - - message_attributes = {k: {"StringValue": json.dumps(v), - "DataType": "String"} - for k, v in kwargs.items() if v} - for target in targets: - self.client.publish(TargetArn=target, Message=message, - Subject=kwargs.get(ATTR_TITLE, - ATTR_TITLE_DEFAULT), - MessageAttributes=message_attributes) diff --git a/homeassistant/components/aws_sqs/__init__.py b/homeassistant/components/aws_sqs/__init__.py deleted file mode 100644 index 79b29a76009a7d..00000000000000 --- a/homeassistant/components/aws_sqs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The aws_sqs component.""" diff --git a/homeassistant/components/aws_sqs/notify.py b/homeassistant/components/aws_sqs/notify.py deleted file mode 100644 index 4c4c831482b813..00000000000000 --- a/homeassistant/components/aws_sqs/notify.py +++ /dev/null @@ -1,81 +0,0 @@ -"""AWS SQS platform for notify component.""" -import json -import logging - -import voluptuous as vol - -from homeassistant.const import CONF_NAME, CONF_PLATFORM -import homeassistant.helpers.config_validation as cv - -from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, - BaseNotificationService) - -_LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ["boto3==1.9.16"] - -CONF_REGION = 'region_name' -CONF_ACCESS_KEY_ID = 'aws_access_key_id' -CONF_SECRET_ACCESS_KEY = 'aws_secret_access_key' -CONF_PROFILE_NAME = 'profile_name' -ATTR_CREDENTIALS = 'credentials' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_REGION, default='us-east-1'): cv.string, - vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, - vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, - vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, -}) - - -def get_service(hass, config, discovery_info=None): - """Get the AWS SQS notification service.""" - _LOGGER.warning( - "aws_sqs notify platform is deprecated, please replace it" - " with aws component. This config will become invalid in version 0.92." - " See https://www.home-assistant.io/components/aws/ for details." - ) - - import boto3 - - aws_config = config.copy() - - del aws_config[CONF_PLATFORM] - del aws_config[CONF_NAME] - - profile = aws_config.get(CONF_PROFILE_NAME) - - if profile is not None: - boto3.setup_default_session(profile_name=profile) - del aws_config[CONF_PROFILE_NAME] - - sqs_client = boto3.client("sqs", **aws_config) - - return AWSSQS(sqs_client) - - -class AWSSQS(BaseNotificationService): - """Implement the notification service for the AWS SQS service.""" - - def __init__(self, sqs_client): - """Initialize the service.""" - self.client = sqs_client - - def send_message(self, message="", **kwargs): - """Send notification to specified SQS ARN.""" - targets = kwargs.get(ATTR_TARGET) - - if not targets: - _LOGGER.info("At least 1 target is required") - return - - for target in targets: - cleaned_kwargs = dict((k, v) for k, v in kwargs.items() if v) - message_body = {"message": message} - message_body.update(cleaned_kwargs) - message_attributes = {} - for key, val in cleaned_kwargs.items(): - message_attributes[key] = {"StringValue": json.dumps(val), - "DataType": "String"} - self.client.send_message(QueueUrl=target, - MessageBody=json.dumps(message_body), - MessageAttributes=message_attributes) diff --git a/requirements_all.txt b/requirements_all.txt index 801f850375dc2f..69430c8cf98fb9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -237,9 +237,6 @@ blockchain==1.4.4 # homeassistant.components.route53 # homeassistant.components.amazon_polly.tts -# homeassistant.components.aws_lambda.notify -# homeassistant.components.aws_sns.notify -# homeassistant.components.aws_sqs.notify boto3==1.9.16 # homeassistant.components.braviatv.media_player From cfe4cf30ad3ce6359420aae77ad5e27cdf45baed Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 21:14:45 -0700 Subject: [PATCH 396/605] Add manifests (#22699) * Add manifests * Update auto name * Update codeowners * Add requirements from platforms * Minor cleanup * Incorporate changes from awarecan PR --- CODEOWNERS | 10 +++++--- homeassistant/components/abode/manifest.json | 10 ++++++++ .../components/acer_projector/manifest.json | 10 ++++++++ .../components/actiontec/manifest.json | 8 ++++++ homeassistant/components/ads/manifest.json | 10 ++++++++ .../components/aftership/manifest.json | 10 ++++++++ .../components/air_quality/manifest.json | 8 ++++++ .../components/airvisual/manifest.json | 12 +++++++++ .../components/aladdin_connect/manifest.json | 10 ++++++++ .../alarm_control_panel/manifest.json | 10 ++++++++ .../components/alarmdecoder/manifest.json | 10 ++++++++ .../components/alarmdotcom/manifest.json | 10 ++++++++ homeassistant/components/alert/manifest.json | 8 ++++++ homeassistant/components/alexa/manifest.json | 10 ++++++++ .../components/alpha_vantage/manifest.json | 12 +++++++++ .../components/amazon_polly/manifest.json | 12 +++++++++ .../components/ambient_station/manifest.json | 12 +++++++++ .../components/amcrest/manifest.json | 12 +++++++++ homeassistant/components/ampio/manifest.json | 10 ++++++++ .../android_ip_webcam/manifest.json | 10 ++++++++ .../components/androidtv/manifest.json | 10 ++++++++ .../components/anel_pwrctrl/manifest.json | 10 ++++++++ .../components/anthemav/manifest.json | 10 ++++++++ .../components/apcupsd/manifest.json | 10 ++++++++ homeassistant/components/api/manifest.json | 12 +++++++++ .../components/api_streams/__init__.py | 1 - homeassistant/components/apns/manifest.json | 10 ++++++++ .../components/apple_tv/manifest.json | 10 ++++++++ .../components/aqualogic/manifest.json | 10 ++++++++ .../components/aquostv/manifest.json | 10 ++++++++ .../components/arduino/manifest.json | 12 +++++++++ homeassistant/components/arest/manifest.json | 10 ++++++++ homeassistant/components/arlo/manifest.json | 10 ++++++++ homeassistant/components/aruba/manifest.json | 10 ++++++++ homeassistant/components/arwn/manifest.json | 8 ++++++ .../components/asterisk_cdr/manifest.json | 8 ++++++ .../components/asterisk_mbox/manifest.json | 10 ++++++++ .../components/asuswrt/manifest.json | 12 +++++++++ homeassistant/components/august/manifest.json | 10 ++++++++ homeassistant/components/aurora/manifest.json | 8 ++++++ homeassistant/components/auth/manifest.json | 12 +++++++++ .../components/automatic/manifest.json | 12 +++++++++ .../components/automation/manifest.json | 12 +++++++++ homeassistant/components/avion/manifest.json | 10 ++++++++ homeassistant/components/awair/manifest.json | 10 ++++++++ homeassistant/components/aws/manifest.json | 13 ++++++++++ .../components/aws_lambda/manifest.json | 10 ++++++++ .../components/aws_sns/manifest.json | 10 ++++++++ .../components/aws_sqs/manifest.json | 10 ++++++++ homeassistant/components/axis/manifest.json | 12 +++++++++ homeassistant/components/baidu/manifest.json | 10 ++++++++ .../components/bayesian/manifest.json | 8 ++++++ .../components/bbb_gpio/manifest.json | 10 ++++++++ homeassistant/components/bbox/manifest.json | 10 ++++++++ homeassistant/components/bh1750/manifest.json | 11 ++++++++ .../components/binary_sensor/manifest.json | 8 ++++++ .../components/bitcoin/manifest.json | 12 +++++++++ .../components/blackbird/manifest.json | 10 ++++++++ homeassistant/components/blink/manifest.json | 12 +++++++++ .../components/blinksticklight/manifest.json | 10 ++++++++ homeassistant/components/blinkt/manifest.json | 10 ++++++++ .../components/blockchain/manifest.json | 10 ++++++++ .../components/bloomsky/manifest.json | 8 ++++++ .../components/bluesound/manifest.json | 10 ++++++++ .../bluetooth_le_tracker/manifest.json | 10 ++++++++ .../bluetooth_tracker/manifest.json | 11 ++++++++ homeassistant/components/bme280/manifest.json | 11 ++++++++ homeassistant/components/bme680/manifest.json | 11 ++++++++ .../bmw_connected_drive/manifest.json | 12 +++++++++ homeassistant/components/bom/manifest.json | 8 ++++++ .../components/braviatv/manifest.json | 12 +++++++++ .../components/broadlink/manifest.json | 12 +++++++++ .../brottsplatskartan/manifest.json | 10 ++++++++ .../components/browser/manifest.json | 8 ++++++ homeassistant/components/brunt/manifest.json | 12 +++++++++ .../components/bt_home_hub_5/manifest.json | 10 ++++++++ .../components/bt_smarthub/manifest.json | 12 +++++++++ .../components/buienradar/manifest.json | 10 ++++++++ homeassistant/components/caldav/manifest.json | 10 ++++++++ .../components/calendar/manifest.json | 10 ++++++++ homeassistant/components/camera/manifest.json | 10 ++++++++ homeassistant/components/canary/manifest.json | 10 ++++++++ homeassistant/components/cast/manifest.json | 10 ++++++++ .../components/cert_expiry/manifest.json | 8 ++++++ .../components/channels/manifest.json | 10 ++++++++ .../components/cisco_ios/manifest.json | 10 ++++++++ .../cisco_mobility_express/manifest.json | 10 ++++++++ .../cisco_webex_teams/manifest.json | 10 ++++++++ .../components/ciscospark/manifest.json | 10 ++++++++ .../components/citybikes/manifest.json | 8 ++++++ .../components/clementine/manifest.json | 10 ++++++++ .../components/clickatell/manifest.json | 8 ++++++ .../components/clicksend/manifest.json | 8 ++++++ .../components/clicksend_tts/manifest.json | 8 ++++++ .../components/climate/manifest.json | 8 ++++++ homeassistant/components/cloud/manifest.json | 14 +++++++++++ .../components/cloudflare/manifest.json | 12 +++++++++ homeassistant/components/cmus/manifest.json | 10 ++++++++ .../components/co2signal/manifest.json | 10 ++++++++ .../components/coinbase/manifest.json | 10 ++++++++ .../components/coinmarketcap/manifest.json | 10 ++++++++ .../comed_hourly_pricing/manifest.json | 8 ++++++ .../components/comfoconnect/manifest.json | 10 ++++++++ .../components/command_line/manifest.json | 8 ++++++ .../components/concord232/manifest.json | 10 ++++++++ homeassistant/components/config/manifest.json | 12 +++++++++ .../components/configurator/manifest.json | 10 ++++++++ .../components/conversation/manifest.json | 12 +++++++++ .../components/coolmaster/manifest.json | 12 +++++++++ .../components/counter/manifest.json | 10 ++++++++ homeassistant/components/cover/manifest.json | 12 +++++++++ .../components/cppm_tracker/manifest.json | 10 ++++++++ .../components/cpuspeed/manifest.json | 12 +++++++++ .../components/crimereports/manifest.json | 10 ++++++++ homeassistant/components/cups/manifest.json | 12 +++++++++ .../components/currencylayer/manifest.json | 8 ++++++ homeassistant/components/daikin/manifest.json | 13 ++++++++++ .../components/danfoss_air/manifest.json | 10 ++++++++ .../components/darksky/manifest.json | 12 +++++++++ .../components/datadog/manifest.json | 10 ++++++++ homeassistant/components/ddwrt/manifest.json | 8 ++++++ homeassistant/components/deconz/manifest.json | 12 +++++++++ homeassistant/components/decora/manifest.json | 11 ++++++++ .../components/decora_wifi/manifest.json | 10 ++++++++ .../components/default_config/manifest.json | 25 +++++++++++++++++++ homeassistant/components/deluge/manifest.json | 10 ++++++++ homeassistant/components/demo/manifest.json | 14 +++++++++++ homeassistant/components/denon/manifest.json | 8 ++++++ .../components/denonavr/manifest.json | 10 ++++++++ .../components/deutsche_bahn/manifest.json | 10 ++++++++ .../device_sun_light_trigger/manifest.json | 12 +++++++++ .../components/device_tracker/manifest.json | 11 ++++++++ homeassistant/components/dht/manifest.json | 10 ++++++++ .../components/dialogflow/manifest.json | 10 ++++++++ .../components/digital_ocean/manifest.json | 12 +++++++++ .../components/digitalloggers/manifest.json | 10 ++++++++ .../components/directv/manifest.json | 10 ++++++++ .../components/discogs/manifest.json | 12 +++++++++ .../components/discord/manifest.json | 10 ++++++++ .../components/discovery/manifest.json | 10 ++++++++ .../components/dlib_face_detect/manifest.json | 10 ++++++++ .../dlib_face_identify/manifest.json | 10 ++++++++ homeassistant/components/dlink/manifest.json | 10 ++++++++ .../components/dlna_dmr/manifest.json | 10 ++++++++ homeassistant/components/dnsip/manifest.json | 10 ++++++++ .../components/dominos/manifest.json | 12 +++++++++ .../components/doorbird/manifest.json | 12 +++++++++ homeassistant/components/dovado/manifest.json | 10 ++++++++ .../components/downloader/manifest.json | 8 ++++++ homeassistant/components/dsmr/manifest.json | 10 ++++++++ .../dte_energy_bridge/manifest.json | 8 ++++++ .../dublin_bus_transport/manifest.json | 8 ++++++ .../components/duckdns/manifest.json | 8 ++++++ .../components/duke_energy/manifest.json | 10 ++++++++ homeassistant/components/dunehd/manifest.json | 10 ++++++++ .../dwd_weather_warnings/manifest.json | 8 ++++++ homeassistant/components/dweet/manifest.json | 12 +++++++++ homeassistant/components/dyson/manifest.json | 10 ++++++++ homeassistant/components/ebox/manifest.json | 10 ++++++++ homeassistant/components/ebusd/manifest.json | 10 ++++++++ .../components/ecoal_boiler/manifest.json | 10 ++++++++ homeassistant/components/ecobee/manifest.json | 10 ++++++++ homeassistant/components/econet/manifest.json | 10 ++++++++ .../components/ecovacs/manifest.json | 12 +++++++++ .../eddystone_temperature/manifest.json | 11 ++++++++ homeassistant/components/edimax/manifest.json | 10 ++++++++ .../components/edp_redy/manifest.json | 12 +++++++++ .../components/ee_brightbox/manifest.json | 10 ++++++++ homeassistant/components/efergy/manifest.json | 8 ++++++ .../components/egardia/manifest.json | 12 +++++++++ .../components/eight_sleep/manifest.json | 12 +++++++++ .../components/eliqonline/manifest.json | 10 ++++++++ homeassistant/components/elkm1/manifest.json | 10 ++++++++ homeassistant/components/emby/manifest.json | 12 +++++++++ .../components/emoncms/manifest.json | 8 ++++++ .../components/emoncms_history/manifest.json | 8 ++++++ .../components/emulated_hue/manifest.json | 10 ++++++++ .../components/emulated_roku/manifest.json | 10 ++++++++ .../components/enigma2/manifest.json | 10 ++++++++ .../components/enocean/manifest.json | 10 ++++++++ .../components/enphase_envoy/manifest.json | 10 ++++++++ .../entur_public_transport/manifest.json | 10 ++++++++ .../components/envirophat/manifest.json | 11 ++++++++ .../components/envisalink/manifest.json | 10 ++++++++ .../components/ephember/manifest.json | 12 +++++++++ homeassistant/components/epson/manifest.json | 10 ++++++++ .../components/eq3btsmart/manifest.json | 13 ++++++++++ .../components/esphome/manifest.json | 12 +++++++++ .../components/etherscan/manifest.json | 10 ++++++++ homeassistant/components/eufy/manifest.json | 10 ++++++++ .../components/everlights/manifest.json | 10 ++++++++ .../components/evohome/manifest.json | 10 ++++++++ .../components/facebook/manifest.json | 8 ++++++ .../components/facebox/manifest.json | 8 ++++++ .../components/fail2ban/manifest.json | 8 ++++++ .../components/familyhub/manifest.json | 10 ++++++++ homeassistant/components/fan/manifest.json | 10 ++++++++ .../components/fastdotcom/manifest.json | 10 ++++++++ homeassistant/components/fedex/manifest.json | 10 ++++++++ .../components/feedreader/manifest.json | 10 ++++++++ homeassistant/components/ffmpeg/manifest.json | 10 ++++++++ .../components/ffmpeg_motion/manifest.json | 8 ++++++ .../components/ffmpeg_noise/manifest.json | 8 ++++++ homeassistant/components/fibaro/manifest.json | 10 ++++++++ homeassistant/components/fido/manifest.json | 10 ++++++++ homeassistant/components/file/manifest.json | 10 ++++++++ .../components/filesize/manifest.json | 8 ++++++ homeassistant/components/filter/manifest.json | 10 ++++++++ homeassistant/components/fints/manifest.json | 10 ++++++++ homeassistant/components/fitbit/manifest.json | 12 +++++++++ homeassistant/components/fixer/manifest.json | 12 +++++++++ homeassistant/components/flexit/manifest.json | 10 ++++++++ homeassistant/components/flic/manifest.json | 10 ++++++++ homeassistant/components/flock/manifest.json | 10 ++++++++ .../components/flunearyou/manifest.json | 12 +++++++++ homeassistant/components/flux/manifest.json | 8 ++++++ .../components/flux_led/manifest.json | 10 ++++++++ homeassistant/components/folder/manifest.json | 8 ++++++ .../components/folder_watcher/manifest.json | 10 ++++++++ homeassistant/components/foobot/manifest.json | 10 ++++++++ homeassistant/components/foscam/manifest.json | 10 ++++++++ .../components/foursquare/manifest.json | 12 +++++++++ .../components/free_mobile/manifest.json | 10 ++++++++ .../components/freebox/manifest.json | 12 +++++++++ .../components/freedns/manifest.json | 8 ++++++ homeassistant/components/fritz/manifest.json | 10 ++++++++ .../components/fritzbox/manifest.json | 10 ++++++++ .../fritzbox_callmonitor/manifest.json | 10 ++++++++ .../fritzbox_netmonitor/manifest.json | 10 ++++++++ .../components/fritzdect/manifest.json | 10 ++++++++ .../components/frontend/manifest.json | 20 +++++++++++++++ .../components/frontier_silicon/manifest.json | 10 ++++++++ .../components/futurenow/manifest.json | 10 ++++++++ .../components/garadget/manifest.json | 8 ++++++ homeassistant/components/gc100/manifest.json | 10 ++++++++ .../components/gearbest/manifest.json | 12 +++++++++ .../components/geizhals/manifest.json | 10 ++++++++ .../components/generic/manifest.json | 8 ++++++ .../generic_thermostat/manifest.json | 8 ++++++ .../components/geo_json_events/manifest.json | 10 ++++++++ .../components/geo_location/manifest.json | 8 ++++++ .../components/geo_rss_events/manifest.json | 10 ++++++++ .../components/geofency/manifest.json | 10 ++++++++ homeassistant/components/github/manifest.json | 10 ++++++++ .../components/gitlab_ci/manifest.json | 10 ++++++++ homeassistant/components/gitter/manifest.json | 12 +++++++++ .../components/glances/manifest.json | 12 +++++++++ homeassistant/components/gntp/manifest.json | 12 +++++++++ .../components/goalfeed/manifest.json | 10 ++++++++ .../components/gogogate2/manifest.json | 10 ++++++++ homeassistant/components/google/manifest.json | 13 ++++++++++ .../components/google_assistant/manifest.json | 10 ++++++++ .../components/google_domains/manifest.json | 8 ++++++ .../components/google_maps/manifest.json | 10 ++++++++ .../components/google_pubsub/manifest.json | 10 ++++++++ .../google_travel_time/manifest.json | 12 +++++++++ .../components/google_wifi/manifest.json | 8 ++++++ .../components/googlehome/manifest.json | 12 +++++++++ homeassistant/components/gpmdp/manifest.json | 10 ++++++++ homeassistant/components/gpsd/manifest.json | 12 +++++++++ .../components/gpslogger/manifest.json | 10 ++++++++ .../components/graphite/manifest.json | 8 ++++++ .../components/greeneye_monitor/manifest.json | 10 ++++++++ .../components/greenwave/manifest.json | 10 ++++++++ homeassistant/components/group/manifest.json | 10 ++++++++ .../components/gstreamer/manifest.json | 10 ++++++++ homeassistant/components/gtfs/manifest.json | 12 +++++++++ homeassistant/components/gtt/manifest.json | 10 ++++++++ .../components/habitica/manifest.json | 10 ++++++++ .../components/hangouts/manifest.json | 10 ++++++++ .../harman_kardon_avr/manifest.json | 10 ++++++++ .../components/harmony/manifest.json | 12 +++++++++ homeassistant/components/hassio/manifest.json | 12 +++++++++ .../components/haveibeenpwned/manifest.json | 8 ++++++ .../components/hddtemp/manifest.json | 8 ++++++ .../components/hdmi_cec/manifest.json | 10 ++++++++ .../components/heatmiser/manifest.json | 10 ++++++++ homeassistant/components/heos/manifest.json | 12 +++++++++ .../components/hikvision/manifest.json | 12 +++++++++ .../components/hikvisioncam/manifest.json | 10 ++++++++ .../components/hipchat/manifest.json | 10 ++++++++ .../components/history/manifest.json | 13 ++++++++++ .../components/history_graph/manifest.json | 12 +++++++++ .../components/history_stats/manifest.json | 8 ++++++ .../components/hitron_coda/manifest.json | 8 ++++++ homeassistant/components/hive/manifest.json | 13 ++++++++++ .../components/hlk_sw16/manifest.json | 10 ++++++++ .../components/homeassistant/manifest.json | 10 ++++++++ .../components/homekit/manifest.json | 12 +++++++++ .../homekit_controller/manifest.json | 10 ++++++++ .../components/homematic/manifest.json | 10 ++++++++ .../homematicip_cloud/manifest.json | 10 ++++++++ .../components/homeworks/manifest.json | 10 ++++++++ .../components/honeywell/manifest.json | 11 ++++++++ homeassistant/components/hook/manifest.json | 8 ++++++ .../components/horizon/manifest.json | 10 ++++++++ homeassistant/components/hp_ilo/manifest.json | 10 ++++++++ homeassistant/components/html5/manifest.json | 12 +++++++++ homeassistant/components/http/manifest.json | 12 +++++++++ homeassistant/components/htu21d/manifest.json | 11 ++++++++ .../components/huawei_lte/manifest.json | 12 +++++++++ .../components/huawei_router/manifest.json | 10 ++++++++ homeassistant/components/hue/manifest.json | 12 +++++++++ .../hunterdouglas_powerview/manifest.json | 10 ++++++++ .../components/hydrawise/manifest.json | 10 ++++++++ .../components/hydroquebec/manifest.json | 10 ++++++++ .../components/hyperion/manifest.json | 8 ++++++ homeassistant/components/ialarm/manifest.json | 10 ++++++++ homeassistant/components/icloud/manifest.json | 10 ++++++++ .../components/idteck_prox/manifest.json | 10 ++++++++ homeassistant/components/ifttt/manifest.json | 12 +++++++++ homeassistant/components/iglo/manifest.json | 10 ++++++++ homeassistant/components/ihc/manifest.json | 11 ++++++++ .../components/image_processing/manifest.json | 10 ++++++++ homeassistant/components/imap/manifest.json | 10 ++++++++ .../imap_email_content/manifest.json | 8 ++++++ .../components/influxdb/manifest.json | 12 +++++++++ .../components/input_boolean/manifest.json | 10 ++++++++ .../components/input_datetime/manifest.json | 10 ++++++++ .../components/input_number/manifest.json | 10 ++++++++ .../components/input_select/manifest.json | 10 ++++++++ .../components/input_text/manifest.json | 10 ++++++++ .../components/insteon/manifest.json | 10 ++++++++ .../components/insteon_local/manifest.json | 8 ++++++ .../components/insteon_plm/manifest.json | 8 ++++++ .../components/integration/manifest.json | 10 ++++++++ .../components/intent_script/manifest.json | 8 ++++++ .../components/introduction/manifest.json | 10 ++++++++ homeassistant/components/ios/manifest.json | 14 +++++++++++ homeassistant/components/iota/manifest.json | 10 ++++++++ homeassistant/components/iperf3/manifest.json | 10 ++++++++ homeassistant/components/ipma/manifest.json | 12 +++++++++ .../irish_rail_transport/manifest.json | 12 +++++++++ .../islamic_prayer_times/manifest.json | 10 ++++++++ homeassistant/components/iss/manifest.json | 10 ++++++++ homeassistant/components/isy994/manifest.json | 10 ++++++++ homeassistant/components/itach/manifest.json | 10 ++++++++ homeassistant/components/itunes/manifest.json | 8 ++++++ .../components/jewish_calendar/manifest.json | 12 +++++++++ .../components/joaoapps_join/manifest.json | 10 ++++++++ .../components/juicenet/manifest.json | 10 ++++++++ homeassistant/components/kankun/manifest.json | 8 ++++++ .../components/keenetic_ndms2/manifest.json | 10 ++++++++ .../components/keyboard/manifest.json | 10 ++++++++ .../components/keyboard_remote/manifest.json | 10 ++++++++ homeassistant/components/kira/manifest.json | 10 ++++++++ homeassistant/components/kiwi/manifest.json | 10 ++++++++ homeassistant/components/knx/manifest.json | 12 +++++++++ homeassistant/components/kodi/manifest.json | 13 ++++++++++ .../components/konnected/manifest.json | 14 +++++++++++ homeassistant/components/kwb/manifest.json | 10 ++++++++ .../components/lacrosse/manifest.json | 10 ++++++++ .../components/lametric/manifest.json | 12 +++++++++ .../components/lannouncer/manifest.json | 8 ++++++ homeassistant/components/lastfm/manifest.json | 10 ++++++++ .../components/launch_library/manifest.json | 12 +++++++++ homeassistant/components/lcn/manifest.json | 10 ++++++++ .../components/lg_netcast/manifest.json | 10 ++++++++ .../components/lg_soundbar/manifest.json | 10 ++++++++ homeassistant/components/lifx/manifest.json | 13 ++++++++++ .../components/lifx_cloud/manifest.json | 10 ++++++++ .../components/lifx_legacy/manifest.json | 12 +++++++++ homeassistant/components/light/manifest.json | 10 ++++++++ .../components/lightwave/manifest.json | 10 ++++++++ .../components/limitlessled/manifest.json | 10 ++++++++ .../components/linksys_ap/manifest.json | 10 ++++++++ .../components/linksys_smart/manifest.json | 8 ++++++ homeassistant/components/linky/manifest.json | 10 ++++++++ homeassistant/components/linode/manifest.json | 10 ++++++++ .../components/linux_battery/manifest.json | 12 +++++++++ homeassistant/components/lirc/manifest.json | 10 ++++++++ .../components/litejet/manifest.json | 10 ++++++++ .../components/liveboxplaytv/manifest.json | 13 ++++++++++ .../llamalab_automate/manifest.json | 8 ++++++ .../components/local_file/manifest.json | 8 ++++++ .../components/locative/manifest.json | 10 ++++++++ homeassistant/components/lock/manifest.json | 10 ++++++++ .../components/lockitron/manifest.json | 8 ++++++ .../components/logbook/manifest.json | 11 ++++++++ .../components/logentries/manifest.json | 8 ++++++ homeassistant/components/logger/manifest.json | 10 ++++++++ .../components/logi_circle/manifest.json | 10 ++++++++ .../components/london_air/manifest.json | 8 ++++++ .../london_underground/manifest.json | 10 ++++++++ .../components/loopenergy/manifest.json | 10 ++++++++ .../components/lovelace/manifest.json | 10 ++++++++ homeassistant/components/luci/manifest.json | 10 ++++++++ .../components/luftdaten/manifest.json | 12 +++++++++ .../components/lupusec/manifest.json | 10 ++++++++ homeassistant/components/lutron/manifest.json | 10 ++++++++ .../components/lutron_caseta/manifest.json | 10 ++++++++ .../components/lw12wifi/manifest.json | 10 ++++++++ homeassistant/components/lyft/manifest.json | 10 ++++++++ .../components/magicseaweed/manifest.json | 10 ++++++++ .../components/mailbox/manifest.json | 10 ++++++++ .../components/mailgun/manifest.json | 12 +++++++++ homeassistant/components/manual/manifest.json | 8 ++++++ .../components/manual_mqtt/manifest.json | 8 ++++++ homeassistant/components/map/manifest.json | 8 ++++++ .../components/marytts/manifest.json | 8 ++++++ .../components/mastodon/manifest.json | 12 +++++++++ homeassistant/components/matrix/manifest.json | 12 +++++++++ .../components/maxcube/manifest.json | 10 ++++++++ .../components/media_extractor/manifest.json | 12 +++++++++ .../components/media_player/manifest.json | 10 ++++++++ .../components/mediaroom/manifest.json | 12 +++++++++ .../components/melissa/manifest.json | 12 +++++++++ homeassistant/components/meraki/manifest.json | 8 ++++++ .../components/message_bird/manifest.json | 10 ++++++++ homeassistant/components/met/manifest.json | 12 +++++++++ .../components/meteo_france/manifest.json | 10 ++++++++ .../components/metoffice/manifest.json | 10 ++++++++ homeassistant/components/mfi/manifest.json | 10 ++++++++ homeassistant/components/mhz19/manifest.json | 10 ++++++++ .../components/microsoft/manifest.json | 10 ++++++++ .../components/microsoft_face/manifest.json | 10 ++++++++ .../microsoft_face_detect/manifest.json | 8 ++++++ .../microsoft_face_identify/manifest.json | 8 ++++++ .../components/miflora/manifest.json | 13 ++++++++++ .../components/mikrotik/manifest.json | 10 ++++++++ homeassistant/components/mill/manifest.json | 12 +++++++++ .../components/min_max/manifest.json | 10 ++++++++ .../components/mitemp_bt/manifest.json | 10 ++++++++ homeassistant/components/mjpeg/manifest.json | 8 ++++++ .../components/mobile_app/manifest.json | 16 ++++++++++++ homeassistant/components/mochad/manifest.json | 10 ++++++++ homeassistant/components/modbus/manifest.json | 10 ++++++++ .../components/modem_callerid/manifest.json | 10 ++++++++ .../components/mold_indicator/manifest.json | 8 ++++++ .../components/monoprice/manifest.json | 12 +++++++++ homeassistant/components/moon/manifest.json | 10 ++++++++ homeassistant/components/mopar/manifest.json | 10 ++++++++ homeassistant/components/mpchc/manifest.json | 8 ++++++ homeassistant/components/mpd/manifest.json | 12 +++++++++ homeassistant/components/mqtt/manifest.json | 13 ++++++++++ .../components/mqtt_eventstream/manifest.json | 10 ++++++++ .../components/mqtt_json/manifest.json | 8 ++++++ .../components/mqtt_room/manifest.json | 8 ++++++ .../components/mqtt_statestream/manifest.json | 10 ++++++++ .../components/mvglive/manifest.json | 10 ++++++++ .../components/mychevy/manifest.json | 10 ++++++++ .../components/mycroft/manifest.json | 10 ++++++++ homeassistant/components/myq/manifest.json | 10 ++++++++ .../components/mysensors/manifest.json | 10 ++++++++ .../components/mystrom/manifest.json | 12 +++++++++ .../components/mythicbeastsdns/manifest.json | 10 ++++++++ homeassistant/components/nad/manifest.json | 10 ++++++++ .../components/namecheapdns/manifest.json | 10 ++++++++ .../components/nanoleaf/manifest.json | 10 ++++++++ homeassistant/components/neato/manifest.json | 10 ++++++++ .../nederlandse_spoorwegen/manifest.json | 10 ++++++++ homeassistant/components/nello/manifest.json | 12 +++++++++ .../components/ness_alarm/manifest.json | 12 +++++++++ homeassistant/components/nest/manifest.json | 12 +++++++++ .../components/netatmo/manifest.json | 12 +++++++++ .../components/netatmo_public/manifest.json | 8 ++++++ .../components/netdata/manifest.json | 12 +++++++++ .../components/netgear/manifest.json | 10 ++++++++ .../components/netgear_lte/manifest.json | 10 ++++++++ homeassistant/components/netio/manifest.json | 10 ++++++++ .../components/neurio_energy/manifest.json | 10 ++++++++ .../components/nfandroidtv/manifest.json | 8 ++++++ .../niko_home_control/manifest.json | 10 ++++++++ homeassistant/components/nilu/manifest.json | 10 ++++++++ .../components/nissan_leaf/manifest.json | 12 +++++++++ .../components/nmap_tracker/manifest.json | 10 ++++++++ homeassistant/components/nmbs/manifest.json | 12 +++++++++ homeassistant/components/no_ip/manifest.json | 10 ++++++++ .../components/noaa_tides/manifest.json | 10 ++++++++ .../components/norway_air/manifest.json | 10 ++++++++ homeassistant/components/notify/manifest.json | 10 ++++++++ .../components/nsw_fuel_station/manifest.json | 12 +++++++++ .../nsw_rural_fire_service_feed/manifest.json | 10 ++++++++ homeassistant/components/nuheat/manifest.json | 10 ++++++++ .../components/nuimo_controller/manifest.json | 10 ++++++++ homeassistant/components/nuki/manifest.json | 12 +++++++++ homeassistant/components/nut/manifest.json | 10 ++++++++ homeassistant/components/nx584/manifest.json | 10 ++++++++ homeassistant/components/nzbget/manifest.json | 8 ++++++ .../components/octoprint/manifest.json | 8 ++++++ homeassistant/components/oem/manifest.json | 10 ++++++++ .../components/ohmconnect/manifest.json | 12 +++++++++ .../components/onboarding/manifest.json | 13 ++++++++++ .../components/onewire/manifest.json | 8 ++++++ homeassistant/components/onkyo/manifest.json | 10 ++++++++ homeassistant/components/onvif/manifest.json | 12 +++++++++ .../components/openalpr_cloud/manifest.json | 8 ++++++ .../components/openalpr_local/manifest.json | 8 ++++++ homeassistant/components/opencv/manifest.json | 10 ++++++++ .../components/openevse/manifest.json | 10 ++++++++ .../openexchangerates/manifest.json | 8 ++++++ .../components/opengarage/manifest.json | 8 ++++++ .../openhardwaremonitor/manifest.json | 8 ++++++ .../components/openhome/manifest.json | 10 ++++++++ .../components/opensensemap/manifest.json | 10 ++++++++ .../components/opensky/manifest.json | 8 ++++++ .../components/opentherm_gw/manifest.json | 10 ++++++++ homeassistant/components/openuv/manifest.json | 12 +++++++++ .../components/openweathermap/manifest.json | 12 +++++++++ homeassistant/components/opple/manifest.json | 10 ++++++++ homeassistant/components/orvibo/manifest.json | 10 ++++++++ .../components/osramlightify/manifest.json | 10 ++++++++ homeassistant/components/otp/manifest.json | 10 ++++++++ homeassistant/components/owlet/manifest.json | 12 +++++++++ .../components/owntracks/manifest.json | 12 +++++++++ .../components/panasonic_bluray/manifest.json | 10 ++++++++ .../components/panasonic_viera/manifest.json | 11 ++++++++ .../components/pandora/manifest.json | 10 ++++++++ .../components/panel_custom/manifest.json | 12 +++++++++ .../components/panel_iframe/manifest.json | 12 +++++++++ homeassistant/components/pencom/manifest.json | 10 ++++++++ .../persistent_notification/manifest.json | 10 ++++++++ homeassistant/components/person/manifest.json | 8 ++++++ .../components/philips_js/manifest.json | 10 ++++++++ .../components/pi_hole/manifest.json | 12 +++++++++ .../components/picotts/manifest.json | 8 ++++++ homeassistant/components/piglow/manifest.json | 10 ++++++++ .../components/pilight/manifest.json | 10 ++++++++ homeassistant/components/ping/manifest.json | 8 ++++++ .../components/pioneer/manifest.json | 8 ++++++ homeassistant/components/pjlink/manifest.json | 10 ++++++++ homeassistant/components/plant/manifest.json | 13 ++++++++++ homeassistant/components/plex/manifest.json | 10 ++++++++ .../components/plum_lightpad/manifest.json | 10 ++++++++ .../components/pocketcasts/manifest.json | 10 ++++++++ homeassistant/components/point/manifest.json | 14 +++++++++++ homeassistant/components/pollen/manifest.json | 13 ++++++++++ homeassistant/components/postnl/manifest.json | 10 ++++++++ .../components/prezzibenzina/manifest.json | 10 ++++++++ .../components/proliphix/manifest.json | 10 ++++++++ .../components/prometheus/manifest.json | 12 +++++++++ homeassistant/components/prowl/manifest.json | 8 ++++++ .../components/proximity/manifest.json | 11 ++++++++ homeassistant/components/proxy/manifest.json | 10 ++++++++ homeassistant/components/ps4/manifest.json | 10 ++++++++ .../pulseaudio_loopback/manifest.json | 8 ++++++ homeassistant/components/push/manifest.json | 10 ++++++++ .../components/pushbullet/manifest.json | 10 ++++++++ .../components/pushetta/manifest.json | 10 ++++++++ .../components/pushover/manifest.json | 10 ++++++++ .../components/pushsafer/manifest.json | 8 ++++++ .../components/pvoutput/manifest.json | 10 ++++++++ homeassistant/components/pyload/manifest.json | 8 ++++++ .../components/python_script/manifest.json | 10 ++++++++ .../components/qbittorrent/manifest.json | 10 ++++++++ homeassistant/components/qnap/manifest.json | 12 +++++++++ homeassistant/components/qrcode/manifest.json | 11 ++++++++ .../components/quantum_gateway/manifest.json | 12 +++++++++ .../components/qwikswitch/manifest.json | 12 +++++++++ homeassistant/components/rachio/manifest.json | 10 ++++++++ homeassistant/components/radarr/manifest.json | 8 ++++++ .../components/radiotherm/manifest.json | 10 ++++++++ .../components/rainbird/manifest.json | 10 ++++++++ .../components/raincloud/manifest.json | 10 ++++++++ .../components/rainmachine/manifest.json | 12 +++++++++ homeassistant/components/random/manifest.json | 10 ++++++++ .../components/raspihats/manifest.json | 11 ++++++++ .../components/raspyrfm/manifest.json | 10 ++++++++ .../components/recollect_waste/manifest.json | 10 ++++++++ .../components/recorder/manifest.json | 10 ++++++++ .../components/recswitch/manifest.json | 10 ++++++++ homeassistant/components/reddit/manifest.json | 10 ++++++++ .../components/rejseplanen/manifest.json | 10 ++++++++ .../remember_the_milk/manifest.json | 11 ++++++++ homeassistant/components/remote/manifest.json | 10 ++++++++ homeassistant/components/rest/manifest.json | 8 ++++++ .../components/rest_command/manifest.json | 8 ++++++ homeassistant/components/rflink/manifest.json | 10 ++++++++ homeassistant/components/rfxtrx/manifest.json | 12 +++++++++ homeassistant/components/ring/manifest.json | 10 ++++++++ homeassistant/components/ripple/manifest.json | 10 ++++++++ .../components/ritassist/manifest.json | 10 ++++++++ .../components/rmvtransport/manifest.json | 12 +++++++++ .../components/rocketchat/manifest.json | 10 ++++++++ homeassistant/components/roku/manifest.json | 10 ++++++++ homeassistant/components/roomba/manifest.json | 12 +++++++++ .../components/route53/manifest.json | 11 ++++++++ homeassistant/components/rova/manifest.json | 10 ++++++++ .../components/rpi_camera/manifest.json | 8 ++++++ .../components/rpi_gpio/manifest.json | 10 ++++++++ .../components/rpi_gpio_pwm/manifest.json | 10 ++++++++ .../components/rpi_pfio/manifest.json | 11 ++++++++ homeassistant/components/rpi_rf/manifest.json | 10 ++++++++ .../rss_feed_template/manifest.json | 10 ++++++++ .../components/rtorrent/manifest.json | 8 ++++++ .../components/russound_rio/manifest.json | 10 ++++++++ .../components/russound_rnet/manifest.json | 10 ++++++++ homeassistant/components/ruter/manifest.json | 12 +++++++++ .../components/sabnzbd/manifest.json | 10 ++++++++ .../components/samsungtv/manifest.json | 11 ++++++++ .../components/satel_integra/manifest.json | 10 ++++++++ homeassistant/components/scene/manifest.json | 10 ++++++++ homeassistant/components/scrape/manifest.json | 12 +++++++++ homeassistant/components/script/manifest.json | 12 +++++++++ .../components/scsgate/manifest.json | 10 ++++++++ homeassistant/components/season/manifest.json | 10 ++++++++ .../components/sendgrid/manifest.json | 10 ++++++++ homeassistant/components/sense/manifest.json | 10 ++++++++ .../components/sensehat/manifest.json | 10 ++++++++ .../components/sensibo/manifest.json | 12 +++++++++ homeassistant/components/sensor/manifest.json | 8 ++++++ homeassistant/components/serial/manifest.json | 12 +++++++++ .../components/serial_pm/manifest.json | 10 ++++++++ homeassistant/components/sesame/manifest.json | 10 ++++++++ .../components/seven_segments/manifest.json | 8 ++++++ .../components/seventeentrack/manifest.json | 12 +++++++++ .../components/shell_command/manifest.json | 10 ++++++++ homeassistant/components/shiftr/manifest.json | 12 +++++++++ homeassistant/components/shodan/manifest.json | 12 +++++++++ .../components/shopping_list/manifest.json | 10 ++++++++ homeassistant/components/sht31/manifest.json | 11 ++++++++ homeassistant/components/sigfox/manifest.json | 8 ++++++ .../components/simplepush/manifest.json | 10 ++++++++ .../components/simplisafe/manifest.json | 12 +++++++++ .../components/simulated/manifest.json | 8 ++++++ .../components/sisyphus/manifest.json | 10 ++++++++ .../components/sky_hub/manifest.json | 8 ++++++ .../components/skybeacon/manifest.json | 10 ++++++++ .../components/skybell/manifest.json | 10 ++++++++ homeassistant/components/slack/manifest.json | 10 ++++++++ .../components/sleepiq/manifest.json | 10 ++++++++ homeassistant/components/sma/manifest.json | 12 +++++++++ .../components/smappee/manifest.json | 10 ++++++++ .../components/smartthings/manifest.json | 15 +++++++++++ homeassistant/components/smhi/manifest.json | 10 ++++++++ homeassistant/components/smtp/manifest.json | 10 ++++++++ .../components/snapcast/manifest.json | 10 ++++++++ homeassistant/components/snips/manifest.json | 10 ++++++++ homeassistant/components/snmp/manifest.json | 10 ++++++++ .../components/sochain/manifest.json | 10 ++++++++ .../components/socialblade/manifest.json | 10 ++++++++ .../components/solaredge/manifest.json | 11 ++++++++ homeassistant/components/sonarr/manifest.json | 8 ++++++ .../components/songpal/manifest.json | 10 ++++++++ homeassistant/components/sonos/manifest.json | 12 +++++++++ .../components/sony_projector/manifest.json | 10 ++++++++ .../components/soundtouch/manifest.json | 10 ++++++++ .../components/spaceapi/manifest.json | 12 +++++++++ homeassistant/components/spc/manifest.json | 10 ++++++++ .../components/speedtestdotnet/manifest.json | 10 ++++++++ homeassistant/components/spider/manifest.json | 12 +++++++++ homeassistant/components/splunk/manifest.json | 8 ++++++ .../components/spotcrime/manifest.json | 10 ++++++++ .../components/spotify/manifest.json | 10 ++++++++ homeassistant/components/sql/manifest.json | 12 +++++++++ .../components/squeezebox/manifest.json | 8 ++++++ .../components/srp_energy/manifest.json | 10 ++++++++ .../components/starlingbank/manifest.json | 10 ++++++++ .../components/startca/manifest.json | 10 ++++++++ .../components/statistics/manifest.json | 10 ++++++++ homeassistant/components/statsd/manifest.json | 10 ++++++++ .../components/steam_online/manifest.json | 10 ++++++++ homeassistant/components/stream/manifest.json | 12 +++++++++ homeassistant/components/stride/manifest.json | 10 ++++++++ homeassistant/components/sun/manifest.json | 10 ++++++++ .../components/supervisord/manifest.json | 8 ++++++ .../swiss_hydrological_data/manifest.json | 12 +++++++++ .../swiss_public_transport/manifest.json | 12 +++++++++ .../components/swisscom/manifest.json | 8 ++++++ homeassistant/components/switch/manifest.json | 10 ++++++++ .../components/switchbot/manifest.json | 12 +++++++++ .../components/switchmate/manifest.json | 12 +++++++++ .../components/syncthru/manifest.json | 10 ++++++++ .../components/synology/manifest.json | 10 ++++++++ .../components/synology_chat/manifest.json | 8 ++++++ .../components/synology_srm/manifest.json | 12 +++++++++ .../components/synologydsm/manifest.json | 10 ++++++++ homeassistant/components/syslog/manifest.json | 10 ++++++++ .../components/system_health/manifest.json | 10 ++++++++ .../components/system_log/manifest.json | 10 ++++++++ .../components/systemmonitor/manifest.json | 10 ++++++++ .../components/sytadin/manifest.json | 12 +++++++++ homeassistant/components/tado/manifest.json | 10 ++++++++ homeassistant/components/tahoma/manifest.json | 12 +++++++++ .../components/tank_utility/manifest.json | 10 ++++++++ .../components/tapsaff/manifest.json | 10 ++++++++ .../components/tautulli/manifest.json | 12 +++++++++ homeassistant/components/tcp/manifest.json | 8 ++++++ .../components/ted5000/manifest.json | 10 ++++++++ .../components/teksavvy/manifest.json | 8 ++++++ .../components/telegram/manifest.json | 8 ++++++ .../components/telegram_bot/manifest.json | 10 ++++++++ .../components/tellduslive/manifest.json | 12 +++++++++ .../components/tellstick/manifest.json | 11 ++++++++ homeassistant/components/telnet/manifest.json | 8 ++++++ homeassistant/components/temper/manifest.json | 10 ++++++++ .../components/template/manifest.json | 10 ++++++++ .../components/tensorflow/manifest.json | 12 +++++++++ homeassistant/components/tesla/manifest.json | 12 +++++++++ homeassistant/components/tfiac/manifest.json | 13 ++++++++++ .../thermoworks_smoke/manifest.json | 11 ++++++++ .../components/thethingsnetwork/manifest.json | 10 ++++++++ .../components/thingspeak/manifest.json | 10 ++++++++ .../components/thinkingcleaner/manifest.json | 10 ++++++++ .../components/thomson/manifest.json | 8 ++++++ .../components/threshold/manifest.json | 10 ++++++++ homeassistant/components/tibber/manifest.json | 12 +++++++++ .../components/tikteck/manifest.json | 10 ++++++++ homeassistant/components/tile/manifest.json | 12 +++++++++ .../components/time_date/manifest.json | 10 ++++++++ homeassistant/components/timer/manifest.json | 8 ++++++ homeassistant/components/tod/manifest.json | 8 ++++++ .../components/todoist/manifest.json | 10 ++++++++ homeassistant/components/tof/manifest.json | 10 ++++++++ homeassistant/components/tomato/manifest.json | 8 ++++++ homeassistant/components/toon/manifest.json | 12 +++++++++ homeassistant/components/torque/manifest.json | 8 ++++++ .../components/totalconnect/manifest.json | 10 ++++++++ .../components/touchline/manifest.json | 10 ++++++++ homeassistant/components/tplink/manifest.json | 13 ++++++++++ .../components/tplink_lte/manifest.json | 10 ++++++++ .../components/traccar/manifest.json | 13 ++++++++++ homeassistant/components/trackr/manifest.json | 10 ++++++++ .../components/tradfri/manifest.json | 12 +++++++++ .../trafikverket_weatherstation/manifest.json | 10 ++++++++ .../components/transmission/manifest.json | 10 ++++++++ .../components/transport_nsw/manifest.json | 10 ++++++++ .../components/travisci/manifest.json | 10 ++++++++ homeassistant/components/trend/manifest.json | 10 ++++++++ homeassistant/components/tts/manifest.json | 14 +++++++++++ homeassistant/components/tuya/manifest.json | 10 ++++++++ homeassistant/components/twilio/manifest.json | 12 +++++++++ .../components/twilio_call/manifest.json | 10 ++++++++ .../components/twilio_sms/manifest.json | 10 ++++++++ homeassistant/components/twitch/manifest.json | 10 ++++++++ .../components/twitter/manifest.json | 10 ++++++++ homeassistant/components/ubee/manifest.json | 10 ++++++++ homeassistant/components/uber/manifest.json | 12 +++++++++ homeassistant/components/ubus/manifest.json | 8 ++++++ .../components/ue_smart_radio/manifest.json | 8 ++++++ .../components/uk_transport/manifest.json | 8 ++++++ homeassistant/components/unifi/manifest.json | 13 ++++++++++ .../components/unifi_direct/manifest.json | 10 ++++++++ .../components/universal/manifest.json | 8 ++++++ .../components/upc_connect/manifest.json | 10 ++++++++ .../components/upcloud/manifest.json | 12 +++++++++ .../components/updater/manifest.json | 12 +++++++++ homeassistant/components/upnp/manifest.json | 12 +++++++++ homeassistant/components/ups/manifest.json | 10 ++++++++ homeassistant/components/uptime/manifest.json | 8 ++++++ .../components/uptimerobot/manifest.json | 12 +++++++++ homeassistant/components/uscis/manifest.json | 10 ++++++++ .../usgs_earthquakes_feed/manifest.json | 10 ++++++++ homeassistant/components/usps/manifest.json | 10 ++++++++ .../components/utility_meter/manifest.json | 10 ++++++++ homeassistant/components/uvc/manifest.json | 10 ++++++++ homeassistant/components/vacuum/manifest.json | 10 ++++++++ .../components/vasttrafik/manifest.json | 10 ++++++++ homeassistant/components/velbus/manifest.json | 10 ++++++++ homeassistant/components/velux/manifest.json | 12 +++++++++ .../components/venstar/manifest.json | 10 ++++++++ homeassistant/components/vera/manifest.json | 10 ++++++++ .../components/verisure/manifest.json | 11 ++++++++ .../components/version/manifest.json | 12 +++++++++ homeassistant/components/vesync/manifest.json | 10 ++++++++ .../components/viaggiatreno/manifest.json | 8 ++++++ homeassistant/components/vizio/manifest.json | 10 ++++++++ homeassistant/components/vlc/manifest.json | 10 ++++++++ .../components/voicerss/manifest.json | 8 ++++++ .../components/volkszaehler/manifest.json | 10 ++++++++ .../components/volumio/manifest.json | 8 ++++++ .../components/volvooncall/manifest.json | 10 ++++++++ homeassistant/components/vultr/manifest.json | 10 ++++++++ .../components/w800rf32/manifest.json | 10 ++++++++ .../components/wake_on_lan/manifest.json | 10 ++++++++ homeassistant/components/waqi/manifest.json | 12 +++++++++ .../components/water_heater/manifest.json | 8 ++++++ .../components/waterfurnace/manifest.json | 10 ++++++++ .../components/watson_iot/manifest.json | 10 ++++++++ .../components/waze_travel_time/manifest.json | 10 ++++++++ .../components/weather/manifest.json | 10 ++++++++ .../components/webhook/manifest.json | 10 ++++++++ .../components/weblink/manifest.json | 10 ++++++++ .../components/webostv/manifest.json | 11 ++++++++ .../components/websocket_api/manifest.json | 12 +++++++++ homeassistant/components/wemo/manifest.json | 12 +++++++++ homeassistant/components/whois/manifest.json | 10 ++++++++ homeassistant/components/wink/manifest.json | 11 ++++++++ .../components/wirelesstag/manifest.json | 10 ++++++++ .../components/workday/manifest.json | 10 ++++++++ .../components/worldclock/manifest.json | 10 ++++++++ .../components/worldtidesinfo/manifest.json | 8 ++++++ .../components/worxlandroid/manifest.json | 8 ++++++ homeassistant/components/wsdot/manifest.json | 8 ++++++ .../components/wunderground/manifest.json | 8 ++++++ .../components/wunderlist/manifest.json | 10 ++++++++ homeassistant/components/x10/manifest.json | 8 ++++++ .../components/xbox_live/manifest.json | 10 ++++++++ homeassistant/components/xeoma/manifest.json | 10 ++++++++ .../components/xfinity/manifest.json | 12 +++++++++ homeassistant/components/xiaomi/manifest.json | 8 ++++++ .../components/xiaomi_aqara/manifest.json | 13 ++++++++++ .../components/xiaomi_miio/manifest.json | 14 +++++++++++ .../components/xiaomi_tv/manifest.json | 12 +++++++++ homeassistant/components/xmpp/manifest.json | 12 +++++++++ homeassistant/components/xs1/manifest.json | 10 ++++++++ .../components/yale_smart_alarm/manifest.json | 10 ++++++++ homeassistant/components/yamaha/manifest.json | 10 ++++++++ .../components/yamaha_musiccast/manifest.json | 12 +++++++++ .../components/yandextts/manifest.json | 8 ++++++ .../components/yeelight/manifest.json | 13 ++++++++++ .../yeelightsunflower/manifest.json | 12 +++++++++ .../components/yessssms/manifest.json | 12 +++++++++ homeassistant/components/yi/manifest.json | 12 +++++++++ homeassistant/components/yr/manifest.json | 10 ++++++++ .../components/yweather/manifest.json | 10 ++++++++ homeassistant/components/zabbix/manifest.json | 10 ++++++++ homeassistant/components/zamg/manifest.json | 8 ++++++ homeassistant/components/zengge/manifest.json | 10 ++++++++ .../components/zeroconf/manifest.json | 14 +++++++++++ .../components/zestimate/manifest.json | 10 ++++++++ homeassistant/components/zha/manifest.json | 17 +++++++++++++ .../components/zhong_hong/manifest.json | 10 ++++++++ homeassistant/components/zigbee/manifest.json | 10 ++++++++ .../ziggo_mediabox_xl/manifest.json | 10 ++++++++ homeassistant/components/zone/manifest.json | 10 ++++++++ .../components/zoneminder/manifest.json | 12 +++++++++ homeassistant/components/zwave/manifest.json | 13 ++++++++++ 818 files changed, 8378 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/abode/manifest.json create mode 100644 homeassistant/components/acer_projector/manifest.json create mode 100644 homeassistant/components/actiontec/manifest.json create mode 100644 homeassistant/components/ads/manifest.json create mode 100644 homeassistant/components/aftership/manifest.json create mode 100644 homeassistant/components/air_quality/manifest.json create mode 100644 homeassistant/components/airvisual/manifest.json create mode 100644 homeassistant/components/aladdin_connect/manifest.json create mode 100644 homeassistant/components/alarm_control_panel/manifest.json create mode 100644 homeassistant/components/alarmdecoder/manifest.json create mode 100644 homeassistant/components/alarmdotcom/manifest.json create mode 100644 homeassistant/components/alert/manifest.json create mode 100644 homeassistant/components/alexa/manifest.json create mode 100644 homeassistant/components/alpha_vantage/manifest.json create mode 100644 homeassistant/components/amazon_polly/manifest.json create mode 100644 homeassistant/components/ambient_station/manifest.json create mode 100644 homeassistant/components/amcrest/manifest.json create mode 100644 homeassistant/components/ampio/manifest.json create mode 100644 homeassistant/components/android_ip_webcam/manifest.json create mode 100644 homeassistant/components/androidtv/manifest.json create mode 100644 homeassistant/components/anel_pwrctrl/manifest.json create mode 100644 homeassistant/components/anthemav/manifest.json create mode 100644 homeassistant/components/apcupsd/manifest.json create mode 100644 homeassistant/components/api/manifest.json delete mode 100644 homeassistant/components/api_streams/__init__.py create mode 100644 homeassistant/components/apns/manifest.json create mode 100644 homeassistant/components/apple_tv/manifest.json create mode 100644 homeassistant/components/aqualogic/manifest.json create mode 100644 homeassistant/components/aquostv/manifest.json create mode 100644 homeassistant/components/arduino/manifest.json create mode 100644 homeassistant/components/arest/manifest.json create mode 100644 homeassistant/components/arlo/manifest.json create mode 100644 homeassistant/components/aruba/manifest.json create mode 100644 homeassistant/components/arwn/manifest.json create mode 100644 homeassistant/components/asterisk_cdr/manifest.json create mode 100644 homeassistant/components/asterisk_mbox/manifest.json create mode 100644 homeassistant/components/asuswrt/manifest.json create mode 100644 homeassistant/components/august/manifest.json create mode 100644 homeassistant/components/aurora/manifest.json create mode 100644 homeassistant/components/auth/manifest.json create mode 100644 homeassistant/components/automatic/manifest.json create mode 100644 homeassistant/components/automation/manifest.json create mode 100644 homeassistant/components/avion/manifest.json create mode 100644 homeassistant/components/awair/manifest.json create mode 100644 homeassistant/components/aws/manifest.json create mode 100644 homeassistant/components/aws_lambda/manifest.json create mode 100644 homeassistant/components/aws_sns/manifest.json create mode 100644 homeassistant/components/aws_sqs/manifest.json create mode 100644 homeassistant/components/axis/manifest.json create mode 100644 homeassistant/components/baidu/manifest.json create mode 100644 homeassistant/components/bayesian/manifest.json create mode 100644 homeassistant/components/bbb_gpio/manifest.json create mode 100644 homeassistant/components/bbox/manifest.json create mode 100644 homeassistant/components/bh1750/manifest.json create mode 100644 homeassistant/components/binary_sensor/manifest.json create mode 100644 homeassistant/components/bitcoin/manifest.json create mode 100644 homeassistant/components/blackbird/manifest.json create mode 100644 homeassistant/components/blink/manifest.json create mode 100644 homeassistant/components/blinksticklight/manifest.json create mode 100644 homeassistant/components/blinkt/manifest.json create mode 100644 homeassistant/components/blockchain/manifest.json create mode 100644 homeassistant/components/bloomsky/manifest.json create mode 100644 homeassistant/components/bluesound/manifest.json create mode 100644 homeassistant/components/bluetooth_le_tracker/manifest.json create mode 100644 homeassistant/components/bluetooth_tracker/manifest.json create mode 100644 homeassistant/components/bme280/manifest.json create mode 100644 homeassistant/components/bme680/manifest.json create mode 100644 homeassistant/components/bmw_connected_drive/manifest.json create mode 100644 homeassistant/components/bom/manifest.json create mode 100644 homeassistant/components/braviatv/manifest.json create mode 100644 homeassistant/components/broadlink/manifest.json create mode 100644 homeassistant/components/brottsplatskartan/manifest.json create mode 100644 homeassistant/components/browser/manifest.json create mode 100644 homeassistant/components/brunt/manifest.json create mode 100644 homeassistant/components/bt_home_hub_5/manifest.json create mode 100644 homeassistant/components/bt_smarthub/manifest.json create mode 100644 homeassistant/components/buienradar/manifest.json create mode 100644 homeassistant/components/caldav/manifest.json create mode 100644 homeassistant/components/calendar/manifest.json create mode 100644 homeassistant/components/camera/manifest.json create mode 100644 homeassistant/components/canary/manifest.json create mode 100644 homeassistant/components/cast/manifest.json create mode 100644 homeassistant/components/cert_expiry/manifest.json create mode 100644 homeassistant/components/channels/manifest.json create mode 100644 homeassistant/components/cisco_ios/manifest.json create mode 100644 homeassistant/components/cisco_mobility_express/manifest.json create mode 100644 homeassistant/components/cisco_webex_teams/manifest.json create mode 100644 homeassistant/components/ciscospark/manifest.json create mode 100644 homeassistant/components/citybikes/manifest.json create mode 100644 homeassistant/components/clementine/manifest.json create mode 100644 homeassistant/components/clickatell/manifest.json create mode 100644 homeassistant/components/clicksend/manifest.json create mode 100644 homeassistant/components/clicksend_tts/manifest.json create mode 100644 homeassistant/components/climate/manifest.json create mode 100644 homeassistant/components/cloud/manifest.json create mode 100644 homeassistant/components/cloudflare/manifest.json create mode 100644 homeassistant/components/cmus/manifest.json create mode 100644 homeassistant/components/co2signal/manifest.json create mode 100644 homeassistant/components/coinbase/manifest.json create mode 100644 homeassistant/components/coinmarketcap/manifest.json create mode 100644 homeassistant/components/comed_hourly_pricing/manifest.json create mode 100644 homeassistant/components/comfoconnect/manifest.json create mode 100644 homeassistant/components/command_line/manifest.json create mode 100644 homeassistant/components/concord232/manifest.json create mode 100644 homeassistant/components/config/manifest.json create mode 100644 homeassistant/components/configurator/manifest.json create mode 100644 homeassistant/components/conversation/manifest.json create mode 100644 homeassistant/components/coolmaster/manifest.json create mode 100644 homeassistant/components/counter/manifest.json create mode 100644 homeassistant/components/cover/manifest.json create mode 100644 homeassistant/components/cppm_tracker/manifest.json create mode 100644 homeassistant/components/cpuspeed/manifest.json create mode 100644 homeassistant/components/crimereports/manifest.json create mode 100644 homeassistant/components/cups/manifest.json create mode 100644 homeassistant/components/currencylayer/manifest.json create mode 100644 homeassistant/components/daikin/manifest.json create mode 100644 homeassistant/components/danfoss_air/manifest.json create mode 100644 homeassistant/components/darksky/manifest.json create mode 100644 homeassistant/components/datadog/manifest.json create mode 100644 homeassistant/components/ddwrt/manifest.json create mode 100644 homeassistant/components/deconz/manifest.json create mode 100644 homeassistant/components/decora/manifest.json create mode 100644 homeassistant/components/decora_wifi/manifest.json create mode 100644 homeassistant/components/default_config/manifest.json create mode 100644 homeassistant/components/deluge/manifest.json create mode 100644 homeassistant/components/demo/manifest.json create mode 100644 homeassistant/components/denon/manifest.json create mode 100644 homeassistant/components/denonavr/manifest.json create mode 100644 homeassistant/components/deutsche_bahn/manifest.json create mode 100644 homeassistant/components/device_sun_light_trigger/manifest.json create mode 100644 homeassistant/components/device_tracker/manifest.json create mode 100644 homeassistant/components/dht/manifest.json create mode 100644 homeassistant/components/dialogflow/manifest.json create mode 100644 homeassistant/components/digital_ocean/manifest.json create mode 100644 homeassistant/components/digitalloggers/manifest.json create mode 100644 homeassistant/components/directv/manifest.json create mode 100644 homeassistant/components/discogs/manifest.json create mode 100644 homeassistant/components/discord/manifest.json create mode 100644 homeassistant/components/discovery/manifest.json create mode 100644 homeassistant/components/dlib_face_detect/manifest.json create mode 100644 homeassistant/components/dlib_face_identify/manifest.json create mode 100644 homeassistant/components/dlink/manifest.json create mode 100644 homeassistant/components/dlna_dmr/manifest.json create mode 100644 homeassistant/components/dnsip/manifest.json create mode 100644 homeassistant/components/dominos/manifest.json create mode 100644 homeassistant/components/doorbird/manifest.json create mode 100644 homeassistant/components/dovado/manifest.json create mode 100644 homeassistant/components/downloader/manifest.json create mode 100644 homeassistant/components/dsmr/manifest.json create mode 100644 homeassistant/components/dte_energy_bridge/manifest.json create mode 100644 homeassistant/components/dublin_bus_transport/manifest.json create mode 100644 homeassistant/components/duckdns/manifest.json create mode 100644 homeassistant/components/duke_energy/manifest.json create mode 100644 homeassistant/components/dunehd/manifest.json create mode 100644 homeassistant/components/dwd_weather_warnings/manifest.json create mode 100644 homeassistant/components/dweet/manifest.json create mode 100644 homeassistant/components/dyson/manifest.json create mode 100644 homeassistant/components/ebox/manifest.json create mode 100644 homeassistant/components/ebusd/manifest.json create mode 100644 homeassistant/components/ecoal_boiler/manifest.json create mode 100644 homeassistant/components/ecobee/manifest.json create mode 100644 homeassistant/components/econet/manifest.json create mode 100644 homeassistant/components/ecovacs/manifest.json create mode 100644 homeassistant/components/eddystone_temperature/manifest.json create mode 100644 homeassistant/components/edimax/manifest.json create mode 100644 homeassistant/components/edp_redy/manifest.json create mode 100644 homeassistant/components/ee_brightbox/manifest.json create mode 100644 homeassistant/components/efergy/manifest.json create mode 100644 homeassistant/components/egardia/manifest.json create mode 100644 homeassistant/components/eight_sleep/manifest.json create mode 100644 homeassistant/components/eliqonline/manifest.json create mode 100644 homeassistant/components/elkm1/manifest.json create mode 100644 homeassistant/components/emby/manifest.json create mode 100644 homeassistant/components/emoncms/manifest.json create mode 100644 homeassistant/components/emoncms_history/manifest.json create mode 100644 homeassistant/components/emulated_hue/manifest.json create mode 100644 homeassistant/components/emulated_roku/manifest.json create mode 100644 homeassistant/components/enigma2/manifest.json create mode 100644 homeassistant/components/enocean/manifest.json create mode 100644 homeassistant/components/enphase_envoy/manifest.json create mode 100644 homeassistant/components/entur_public_transport/manifest.json create mode 100644 homeassistant/components/envirophat/manifest.json create mode 100644 homeassistant/components/envisalink/manifest.json create mode 100644 homeassistant/components/ephember/manifest.json create mode 100644 homeassistant/components/epson/manifest.json create mode 100644 homeassistant/components/eq3btsmart/manifest.json create mode 100644 homeassistant/components/esphome/manifest.json create mode 100644 homeassistant/components/etherscan/manifest.json create mode 100644 homeassistant/components/eufy/manifest.json create mode 100644 homeassistant/components/everlights/manifest.json create mode 100644 homeassistant/components/evohome/manifest.json create mode 100644 homeassistant/components/facebook/manifest.json create mode 100644 homeassistant/components/facebox/manifest.json create mode 100644 homeassistant/components/fail2ban/manifest.json create mode 100644 homeassistant/components/familyhub/manifest.json create mode 100644 homeassistant/components/fan/manifest.json create mode 100644 homeassistant/components/fastdotcom/manifest.json create mode 100644 homeassistant/components/fedex/manifest.json create mode 100644 homeassistant/components/feedreader/manifest.json create mode 100644 homeassistant/components/ffmpeg/manifest.json create mode 100644 homeassistant/components/ffmpeg_motion/manifest.json create mode 100644 homeassistant/components/ffmpeg_noise/manifest.json create mode 100644 homeassistant/components/fibaro/manifest.json create mode 100644 homeassistant/components/fido/manifest.json create mode 100644 homeassistant/components/file/manifest.json create mode 100644 homeassistant/components/filesize/manifest.json create mode 100644 homeassistant/components/filter/manifest.json create mode 100644 homeassistant/components/fints/manifest.json create mode 100644 homeassistant/components/fitbit/manifest.json create mode 100644 homeassistant/components/fixer/manifest.json create mode 100644 homeassistant/components/flexit/manifest.json create mode 100644 homeassistant/components/flic/manifest.json create mode 100644 homeassistant/components/flock/manifest.json create mode 100644 homeassistant/components/flunearyou/manifest.json create mode 100644 homeassistant/components/flux/manifest.json create mode 100644 homeassistant/components/flux_led/manifest.json create mode 100644 homeassistant/components/folder/manifest.json create mode 100644 homeassistant/components/folder_watcher/manifest.json create mode 100644 homeassistant/components/foobot/manifest.json create mode 100644 homeassistant/components/foscam/manifest.json create mode 100644 homeassistant/components/foursquare/manifest.json create mode 100644 homeassistant/components/free_mobile/manifest.json create mode 100644 homeassistant/components/freebox/manifest.json create mode 100644 homeassistant/components/freedns/manifest.json create mode 100644 homeassistant/components/fritz/manifest.json create mode 100644 homeassistant/components/fritzbox/manifest.json create mode 100644 homeassistant/components/fritzbox_callmonitor/manifest.json create mode 100644 homeassistant/components/fritzbox_netmonitor/manifest.json create mode 100644 homeassistant/components/fritzdect/manifest.json create mode 100644 homeassistant/components/frontend/manifest.json create mode 100644 homeassistant/components/frontier_silicon/manifest.json create mode 100644 homeassistant/components/futurenow/manifest.json create mode 100644 homeassistant/components/garadget/manifest.json create mode 100644 homeassistant/components/gc100/manifest.json create mode 100644 homeassistant/components/gearbest/manifest.json create mode 100644 homeassistant/components/geizhals/manifest.json create mode 100644 homeassistant/components/generic/manifest.json create mode 100644 homeassistant/components/generic_thermostat/manifest.json create mode 100644 homeassistant/components/geo_json_events/manifest.json create mode 100644 homeassistant/components/geo_location/manifest.json create mode 100644 homeassistant/components/geo_rss_events/manifest.json create mode 100644 homeassistant/components/geofency/manifest.json create mode 100644 homeassistant/components/github/manifest.json create mode 100644 homeassistant/components/gitlab_ci/manifest.json create mode 100644 homeassistant/components/gitter/manifest.json create mode 100644 homeassistant/components/glances/manifest.json create mode 100644 homeassistant/components/gntp/manifest.json create mode 100644 homeassistant/components/goalfeed/manifest.json create mode 100644 homeassistant/components/gogogate2/manifest.json create mode 100644 homeassistant/components/google/manifest.json create mode 100644 homeassistant/components/google_assistant/manifest.json create mode 100644 homeassistant/components/google_domains/manifest.json create mode 100644 homeassistant/components/google_maps/manifest.json create mode 100644 homeassistant/components/google_pubsub/manifest.json create mode 100644 homeassistant/components/google_travel_time/manifest.json create mode 100644 homeassistant/components/google_wifi/manifest.json create mode 100644 homeassistant/components/googlehome/manifest.json create mode 100644 homeassistant/components/gpmdp/manifest.json create mode 100644 homeassistant/components/gpsd/manifest.json create mode 100644 homeassistant/components/gpslogger/manifest.json create mode 100644 homeassistant/components/graphite/manifest.json create mode 100644 homeassistant/components/greeneye_monitor/manifest.json create mode 100644 homeassistant/components/greenwave/manifest.json create mode 100644 homeassistant/components/group/manifest.json create mode 100644 homeassistant/components/gstreamer/manifest.json create mode 100644 homeassistant/components/gtfs/manifest.json create mode 100644 homeassistant/components/gtt/manifest.json create mode 100644 homeassistant/components/habitica/manifest.json create mode 100644 homeassistant/components/hangouts/manifest.json create mode 100644 homeassistant/components/harman_kardon_avr/manifest.json create mode 100644 homeassistant/components/harmony/manifest.json create mode 100644 homeassistant/components/hassio/manifest.json create mode 100644 homeassistant/components/haveibeenpwned/manifest.json create mode 100644 homeassistant/components/hddtemp/manifest.json create mode 100644 homeassistant/components/hdmi_cec/manifest.json create mode 100644 homeassistant/components/heatmiser/manifest.json create mode 100644 homeassistant/components/heos/manifest.json create mode 100644 homeassistant/components/hikvision/manifest.json create mode 100644 homeassistant/components/hikvisioncam/manifest.json create mode 100644 homeassistant/components/hipchat/manifest.json create mode 100644 homeassistant/components/history/manifest.json create mode 100644 homeassistant/components/history_graph/manifest.json create mode 100644 homeassistant/components/history_stats/manifest.json create mode 100644 homeassistant/components/hitron_coda/manifest.json create mode 100644 homeassistant/components/hive/manifest.json create mode 100644 homeassistant/components/hlk_sw16/manifest.json create mode 100644 homeassistant/components/homeassistant/manifest.json create mode 100644 homeassistant/components/homekit/manifest.json create mode 100644 homeassistant/components/homekit_controller/manifest.json create mode 100644 homeassistant/components/homematic/manifest.json create mode 100644 homeassistant/components/homematicip_cloud/manifest.json create mode 100644 homeassistant/components/homeworks/manifest.json create mode 100644 homeassistant/components/honeywell/manifest.json create mode 100644 homeassistant/components/hook/manifest.json create mode 100644 homeassistant/components/horizon/manifest.json create mode 100644 homeassistant/components/hp_ilo/manifest.json create mode 100644 homeassistant/components/html5/manifest.json create mode 100644 homeassistant/components/http/manifest.json create mode 100644 homeassistant/components/htu21d/manifest.json create mode 100644 homeassistant/components/huawei_lte/manifest.json create mode 100644 homeassistant/components/huawei_router/manifest.json create mode 100644 homeassistant/components/hue/manifest.json create mode 100644 homeassistant/components/hunterdouglas_powerview/manifest.json create mode 100644 homeassistant/components/hydrawise/manifest.json create mode 100644 homeassistant/components/hydroquebec/manifest.json create mode 100644 homeassistant/components/hyperion/manifest.json create mode 100644 homeassistant/components/ialarm/manifest.json create mode 100644 homeassistant/components/icloud/manifest.json create mode 100644 homeassistant/components/idteck_prox/manifest.json create mode 100644 homeassistant/components/ifttt/manifest.json create mode 100644 homeassistant/components/iglo/manifest.json create mode 100644 homeassistant/components/ihc/manifest.json create mode 100644 homeassistant/components/image_processing/manifest.json create mode 100644 homeassistant/components/imap/manifest.json create mode 100644 homeassistant/components/imap_email_content/manifest.json create mode 100644 homeassistant/components/influxdb/manifest.json create mode 100644 homeassistant/components/input_boolean/manifest.json create mode 100644 homeassistant/components/input_datetime/manifest.json create mode 100644 homeassistant/components/input_number/manifest.json create mode 100644 homeassistant/components/input_select/manifest.json create mode 100644 homeassistant/components/input_text/manifest.json create mode 100644 homeassistant/components/insteon/manifest.json create mode 100644 homeassistant/components/insteon_local/manifest.json create mode 100644 homeassistant/components/insteon_plm/manifest.json create mode 100644 homeassistant/components/integration/manifest.json create mode 100644 homeassistant/components/intent_script/manifest.json create mode 100644 homeassistant/components/introduction/manifest.json create mode 100644 homeassistant/components/ios/manifest.json create mode 100644 homeassistant/components/iota/manifest.json create mode 100644 homeassistant/components/iperf3/manifest.json create mode 100644 homeassistant/components/ipma/manifest.json create mode 100644 homeassistant/components/irish_rail_transport/manifest.json create mode 100644 homeassistant/components/islamic_prayer_times/manifest.json create mode 100644 homeassistant/components/iss/manifest.json create mode 100644 homeassistant/components/isy994/manifest.json create mode 100644 homeassistant/components/itach/manifest.json create mode 100644 homeassistant/components/itunes/manifest.json create mode 100644 homeassistant/components/jewish_calendar/manifest.json create mode 100644 homeassistant/components/joaoapps_join/manifest.json create mode 100644 homeassistant/components/juicenet/manifest.json create mode 100644 homeassistant/components/kankun/manifest.json create mode 100644 homeassistant/components/keenetic_ndms2/manifest.json create mode 100644 homeassistant/components/keyboard/manifest.json create mode 100644 homeassistant/components/keyboard_remote/manifest.json create mode 100644 homeassistant/components/kira/manifest.json create mode 100644 homeassistant/components/kiwi/manifest.json create mode 100644 homeassistant/components/knx/manifest.json create mode 100644 homeassistant/components/kodi/manifest.json create mode 100644 homeassistant/components/konnected/manifest.json create mode 100644 homeassistant/components/kwb/manifest.json create mode 100644 homeassistant/components/lacrosse/manifest.json create mode 100644 homeassistant/components/lametric/manifest.json create mode 100644 homeassistant/components/lannouncer/manifest.json create mode 100644 homeassistant/components/lastfm/manifest.json create mode 100644 homeassistant/components/launch_library/manifest.json create mode 100644 homeassistant/components/lcn/manifest.json create mode 100644 homeassistant/components/lg_netcast/manifest.json create mode 100644 homeassistant/components/lg_soundbar/manifest.json create mode 100644 homeassistant/components/lifx/manifest.json create mode 100644 homeassistant/components/lifx_cloud/manifest.json create mode 100644 homeassistant/components/lifx_legacy/manifest.json create mode 100644 homeassistant/components/light/manifest.json create mode 100644 homeassistant/components/lightwave/manifest.json create mode 100644 homeassistant/components/limitlessled/manifest.json create mode 100644 homeassistant/components/linksys_ap/manifest.json create mode 100644 homeassistant/components/linksys_smart/manifest.json create mode 100644 homeassistant/components/linky/manifest.json create mode 100644 homeassistant/components/linode/manifest.json create mode 100644 homeassistant/components/linux_battery/manifest.json create mode 100644 homeassistant/components/lirc/manifest.json create mode 100644 homeassistant/components/litejet/manifest.json create mode 100644 homeassistant/components/liveboxplaytv/manifest.json create mode 100644 homeassistant/components/llamalab_automate/manifest.json create mode 100644 homeassistant/components/local_file/manifest.json create mode 100644 homeassistant/components/locative/manifest.json create mode 100644 homeassistant/components/lock/manifest.json create mode 100644 homeassistant/components/lockitron/manifest.json create mode 100644 homeassistant/components/logbook/manifest.json create mode 100644 homeassistant/components/logentries/manifest.json create mode 100644 homeassistant/components/logger/manifest.json create mode 100644 homeassistant/components/logi_circle/manifest.json create mode 100644 homeassistant/components/london_air/manifest.json create mode 100644 homeassistant/components/london_underground/manifest.json create mode 100644 homeassistant/components/loopenergy/manifest.json create mode 100644 homeassistant/components/lovelace/manifest.json create mode 100644 homeassistant/components/luci/manifest.json create mode 100644 homeassistant/components/luftdaten/manifest.json create mode 100644 homeassistant/components/lupusec/manifest.json create mode 100644 homeassistant/components/lutron/manifest.json create mode 100644 homeassistant/components/lutron_caseta/manifest.json create mode 100644 homeassistant/components/lw12wifi/manifest.json create mode 100644 homeassistant/components/lyft/manifest.json create mode 100644 homeassistant/components/magicseaweed/manifest.json create mode 100644 homeassistant/components/mailbox/manifest.json create mode 100644 homeassistant/components/mailgun/manifest.json create mode 100644 homeassistant/components/manual/manifest.json create mode 100644 homeassistant/components/manual_mqtt/manifest.json create mode 100644 homeassistant/components/map/manifest.json create mode 100644 homeassistant/components/marytts/manifest.json create mode 100644 homeassistant/components/mastodon/manifest.json create mode 100644 homeassistant/components/matrix/manifest.json create mode 100644 homeassistant/components/maxcube/manifest.json create mode 100644 homeassistant/components/media_extractor/manifest.json create mode 100644 homeassistant/components/media_player/manifest.json create mode 100644 homeassistant/components/mediaroom/manifest.json create mode 100644 homeassistant/components/melissa/manifest.json create mode 100644 homeassistant/components/meraki/manifest.json create mode 100644 homeassistant/components/message_bird/manifest.json create mode 100644 homeassistant/components/met/manifest.json create mode 100644 homeassistant/components/meteo_france/manifest.json create mode 100644 homeassistant/components/metoffice/manifest.json create mode 100644 homeassistant/components/mfi/manifest.json create mode 100644 homeassistant/components/mhz19/manifest.json create mode 100644 homeassistant/components/microsoft/manifest.json create mode 100644 homeassistant/components/microsoft_face/manifest.json create mode 100644 homeassistant/components/microsoft_face_detect/manifest.json create mode 100644 homeassistant/components/microsoft_face_identify/manifest.json create mode 100644 homeassistant/components/miflora/manifest.json create mode 100644 homeassistant/components/mikrotik/manifest.json create mode 100644 homeassistant/components/mill/manifest.json create mode 100644 homeassistant/components/min_max/manifest.json create mode 100644 homeassistant/components/mitemp_bt/manifest.json create mode 100644 homeassistant/components/mjpeg/manifest.json create mode 100644 homeassistant/components/mobile_app/manifest.json create mode 100644 homeassistant/components/mochad/manifest.json create mode 100644 homeassistant/components/modbus/manifest.json create mode 100644 homeassistant/components/modem_callerid/manifest.json create mode 100644 homeassistant/components/mold_indicator/manifest.json create mode 100644 homeassistant/components/monoprice/manifest.json create mode 100644 homeassistant/components/moon/manifest.json create mode 100644 homeassistant/components/mopar/manifest.json create mode 100644 homeassistant/components/mpchc/manifest.json create mode 100644 homeassistant/components/mpd/manifest.json create mode 100644 homeassistant/components/mqtt/manifest.json create mode 100644 homeassistant/components/mqtt_eventstream/manifest.json create mode 100644 homeassistant/components/mqtt_json/manifest.json create mode 100644 homeassistant/components/mqtt_room/manifest.json create mode 100644 homeassistant/components/mqtt_statestream/manifest.json create mode 100644 homeassistant/components/mvglive/manifest.json create mode 100644 homeassistant/components/mychevy/manifest.json create mode 100644 homeassistant/components/mycroft/manifest.json create mode 100644 homeassistant/components/myq/manifest.json create mode 100644 homeassistant/components/mysensors/manifest.json create mode 100644 homeassistant/components/mystrom/manifest.json create mode 100644 homeassistant/components/mythicbeastsdns/manifest.json create mode 100644 homeassistant/components/nad/manifest.json create mode 100644 homeassistant/components/namecheapdns/manifest.json create mode 100644 homeassistant/components/nanoleaf/manifest.json create mode 100644 homeassistant/components/neato/manifest.json create mode 100644 homeassistant/components/nederlandse_spoorwegen/manifest.json create mode 100644 homeassistant/components/nello/manifest.json create mode 100644 homeassistant/components/ness_alarm/manifest.json create mode 100644 homeassistant/components/nest/manifest.json create mode 100644 homeassistant/components/netatmo/manifest.json create mode 100644 homeassistant/components/netatmo_public/manifest.json create mode 100644 homeassistant/components/netdata/manifest.json create mode 100644 homeassistant/components/netgear/manifest.json create mode 100644 homeassistant/components/netgear_lte/manifest.json create mode 100644 homeassistant/components/netio/manifest.json create mode 100644 homeassistant/components/neurio_energy/manifest.json create mode 100644 homeassistant/components/nfandroidtv/manifest.json create mode 100644 homeassistant/components/niko_home_control/manifest.json create mode 100644 homeassistant/components/nilu/manifest.json create mode 100644 homeassistant/components/nissan_leaf/manifest.json create mode 100644 homeassistant/components/nmap_tracker/manifest.json create mode 100644 homeassistant/components/nmbs/manifest.json create mode 100644 homeassistant/components/no_ip/manifest.json create mode 100644 homeassistant/components/noaa_tides/manifest.json create mode 100644 homeassistant/components/norway_air/manifest.json create mode 100644 homeassistant/components/notify/manifest.json create mode 100644 homeassistant/components/nsw_fuel_station/manifest.json create mode 100644 homeassistant/components/nsw_rural_fire_service_feed/manifest.json create mode 100644 homeassistant/components/nuheat/manifest.json create mode 100644 homeassistant/components/nuimo_controller/manifest.json create mode 100644 homeassistant/components/nuki/manifest.json create mode 100644 homeassistant/components/nut/manifest.json create mode 100644 homeassistant/components/nx584/manifest.json create mode 100644 homeassistant/components/nzbget/manifest.json create mode 100644 homeassistant/components/octoprint/manifest.json create mode 100644 homeassistant/components/oem/manifest.json create mode 100644 homeassistant/components/ohmconnect/manifest.json create mode 100644 homeassistant/components/onboarding/manifest.json create mode 100644 homeassistant/components/onewire/manifest.json create mode 100644 homeassistant/components/onkyo/manifest.json create mode 100644 homeassistant/components/onvif/manifest.json create mode 100644 homeassistant/components/openalpr_cloud/manifest.json create mode 100644 homeassistant/components/openalpr_local/manifest.json create mode 100644 homeassistant/components/opencv/manifest.json create mode 100644 homeassistant/components/openevse/manifest.json create mode 100644 homeassistant/components/openexchangerates/manifest.json create mode 100644 homeassistant/components/opengarage/manifest.json create mode 100644 homeassistant/components/openhardwaremonitor/manifest.json create mode 100644 homeassistant/components/openhome/manifest.json create mode 100644 homeassistant/components/opensensemap/manifest.json create mode 100644 homeassistant/components/opensky/manifest.json create mode 100644 homeassistant/components/opentherm_gw/manifest.json create mode 100644 homeassistant/components/openuv/manifest.json create mode 100644 homeassistant/components/openweathermap/manifest.json create mode 100644 homeassistant/components/opple/manifest.json create mode 100644 homeassistant/components/orvibo/manifest.json create mode 100644 homeassistant/components/osramlightify/manifest.json create mode 100644 homeassistant/components/otp/manifest.json create mode 100644 homeassistant/components/owlet/manifest.json create mode 100644 homeassistant/components/owntracks/manifest.json create mode 100644 homeassistant/components/panasonic_bluray/manifest.json create mode 100644 homeassistant/components/panasonic_viera/manifest.json create mode 100644 homeassistant/components/pandora/manifest.json create mode 100644 homeassistant/components/panel_custom/manifest.json create mode 100644 homeassistant/components/panel_iframe/manifest.json create mode 100644 homeassistant/components/pencom/manifest.json create mode 100644 homeassistant/components/persistent_notification/manifest.json create mode 100644 homeassistant/components/person/manifest.json create mode 100644 homeassistant/components/philips_js/manifest.json create mode 100644 homeassistant/components/pi_hole/manifest.json create mode 100644 homeassistant/components/picotts/manifest.json create mode 100644 homeassistant/components/piglow/manifest.json create mode 100644 homeassistant/components/pilight/manifest.json create mode 100644 homeassistant/components/ping/manifest.json create mode 100644 homeassistant/components/pioneer/manifest.json create mode 100644 homeassistant/components/pjlink/manifest.json create mode 100644 homeassistant/components/plant/manifest.json create mode 100644 homeassistant/components/plex/manifest.json create mode 100644 homeassistant/components/plum_lightpad/manifest.json create mode 100644 homeassistant/components/pocketcasts/manifest.json create mode 100644 homeassistant/components/point/manifest.json create mode 100644 homeassistant/components/pollen/manifest.json create mode 100644 homeassistant/components/postnl/manifest.json create mode 100644 homeassistant/components/prezzibenzina/manifest.json create mode 100644 homeassistant/components/proliphix/manifest.json create mode 100644 homeassistant/components/prometheus/manifest.json create mode 100644 homeassistant/components/prowl/manifest.json create mode 100644 homeassistant/components/proximity/manifest.json create mode 100644 homeassistant/components/proxy/manifest.json create mode 100644 homeassistant/components/ps4/manifest.json create mode 100644 homeassistant/components/pulseaudio_loopback/manifest.json create mode 100644 homeassistant/components/push/manifest.json create mode 100644 homeassistant/components/pushbullet/manifest.json create mode 100644 homeassistant/components/pushetta/manifest.json create mode 100644 homeassistant/components/pushover/manifest.json create mode 100644 homeassistant/components/pushsafer/manifest.json create mode 100644 homeassistant/components/pvoutput/manifest.json create mode 100644 homeassistant/components/pyload/manifest.json create mode 100644 homeassistant/components/python_script/manifest.json create mode 100644 homeassistant/components/qbittorrent/manifest.json create mode 100644 homeassistant/components/qnap/manifest.json create mode 100644 homeassistant/components/qrcode/manifest.json create mode 100644 homeassistant/components/quantum_gateway/manifest.json create mode 100644 homeassistant/components/qwikswitch/manifest.json create mode 100644 homeassistant/components/rachio/manifest.json create mode 100644 homeassistant/components/radarr/manifest.json create mode 100644 homeassistant/components/radiotherm/manifest.json create mode 100644 homeassistant/components/rainbird/manifest.json create mode 100644 homeassistant/components/raincloud/manifest.json create mode 100644 homeassistant/components/rainmachine/manifest.json create mode 100644 homeassistant/components/random/manifest.json create mode 100644 homeassistant/components/raspihats/manifest.json create mode 100644 homeassistant/components/raspyrfm/manifest.json create mode 100644 homeassistant/components/recollect_waste/manifest.json create mode 100644 homeassistant/components/recorder/manifest.json create mode 100644 homeassistant/components/recswitch/manifest.json create mode 100644 homeassistant/components/reddit/manifest.json create mode 100644 homeassistant/components/rejseplanen/manifest.json create mode 100644 homeassistant/components/remember_the_milk/manifest.json create mode 100644 homeassistant/components/remote/manifest.json create mode 100644 homeassistant/components/rest/manifest.json create mode 100644 homeassistant/components/rest_command/manifest.json create mode 100644 homeassistant/components/rflink/manifest.json create mode 100644 homeassistant/components/rfxtrx/manifest.json create mode 100644 homeassistant/components/ring/manifest.json create mode 100644 homeassistant/components/ripple/manifest.json create mode 100644 homeassistant/components/ritassist/manifest.json create mode 100644 homeassistant/components/rmvtransport/manifest.json create mode 100644 homeassistant/components/rocketchat/manifest.json create mode 100644 homeassistant/components/roku/manifest.json create mode 100644 homeassistant/components/roomba/manifest.json create mode 100644 homeassistant/components/route53/manifest.json create mode 100644 homeassistant/components/rova/manifest.json create mode 100644 homeassistant/components/rpi_camera/manifest.json create mode 100644 homeassistant/components/rpi_gpio/manifest.json create mode 100644 homeassistant/components/rpi_gpio_pwm/manifest.json create mode 100644 homeassistant/components/rpi_pfio/manifest.json create mode 100644 homeassistant/components/rpi_rf/manifest.json create mode 100644 homeassistant/components/rss_feed_template/manifest.json create mode 100644 homeassistant/components/rtorrent/manifest.json create mode 100644 homeassistant/components/russound_rio/manifest.json create mode 100644 homeassistant/components/russound_rnet/manifest.json create mode 100644 homeassistant/components/ruter/manifest.json create mode 100644 homeassistant/components/sabnzbd/manifest.json create mode 100644 homeassistant/components/samsungtv/manifest.json create mode 100644 homeassistant/components/satel_integra/manifest.json create mode 100644 homeassistant/components/scene/manifest.json create mode 100644 homeassistant/components/scrape/manifest.json create mode 100644 homeassistant/components/script/manifest.json create mode 100644 homeassistant/components/scsgate/manifest.json create mode 100644 homeassistant/components/season/manifest.json create mode 100644 homeassistant/components/sendgrid/manifest.json create mode 100644 homeassistant/components/sense/manifest.json create mode 100644 homeassistant/components/sensehat/manifest.json create mode 100644 homeassistant/components/sensibo/manifest.json create mode 100644 homeassistant/components/sensor/manifest.json create mode 100644 homeassistant/components/serial/manifest.json create mode 100644 homeassistant/components/serial_pm/manifest.json create mode 100644 homeassistant/components/sesame/manifest.json create mode 100644 homeassistant/components/seven_segments/manifest.json create mode 100644 homeassistant/components/seventeentrack/manifest.json create mode 100644 homeassistant/components/shell_command/manifest.json create mode 100644 homeassistant/components/shiftr/manifest.json create mode 100644 homeassistant/components/shodan/manifest.json create mode 100644 homeassistant/components/shopping_list/manifest.json create mode 100644 homeassistant/components/sht31/manifest.json create mode 100644 homeassistant/components/sigfox/manifest.json create mode 100644 homeassistant/components/simplepush/manifest.json create mode 100644 homeassistant/components/simplisafe/manifest.json create mode 100644 homeassistant/components/simulated/manifest.json create mode 100644 homeassistant/components/sisyphus/manifest.json create mode 100644 homeassistant/components/sky_hub/manifest.json create mode 100644 homeassistant/components/skybeacon/manifest.json create mode 100644 homeassistant/components/skybell/manifest.json create mode 100644 homeassistant/components/slack/manifest.json create mode 100644 homeassistant/components/sleepiq/manifest.json create mode 100644 homeassistant/components/sma/manifest.json create mode 100644 homeassistant/components/smappee/manifest.json create mode 100644 homeassistant/components/smartthings/manifest.json create mode 100644 homeassistant/components/smhi/manifest.json create mode 100644 homeassistant/components/smtp/manifest.json create mode 100644 homeassistant/components/snapcast/manifest.json create mode 100644 homeassistant/components/snips/manifest.json create mode 100644 homeassistant/components/snmp/manifest.json create mode 100644 homeassistant/components/sochain/manifest.json create mode 100644 homeassistant/components/socialblade/manifest.json create mode 100644 homeassistant/components/solaredge/manifest.json create mode 100644 homeassistant/components/sonarr/manifest.json create mode 100644 homeassistant/components/songpal/manifest.json create mode 100644 homeassistant/components/sonos/manifest.json create mode 100644 homeassistant/components/sony_projector/manifest.json create mode 100644 homeassistant/components/soundtouch/manifest.json create mode 100644 homeassistant/components/spaceapi/manifest.json create mode 100644 homeassistant/components/spc/manifest.json create mode 100644 homeassistant/components/speedtestdotnet/manifest.json create mode 100644 homeassistant/components/spider/manifest.json create mode 100644 homeassistant/components/splunk/manifest.json create mode 100644 homeassistant/components/spotcrime/manifest.json create mode 100644 homeassistant/components/spotify/manifest.json create mode 100644 homeassistant/components/sql/manifest.json create mode 100644 homeassistant/components/squeezebox/manifest.json create mode 100644 homeassistant/components/srp_energy/manifest.json create mode 100644 homeassistant/components/starlingbank/manifest.json create mode 100644 homeassistant/components/startca/manifest.json create mode 100644 homeassistant/components/statistics/manifest.json create mode 100644 homeassistant/components/statsd/manifest.json create mode 100644 homeassistant/components/steam_online/manifest.json create mode 100644 homeassistant/components/stream/manifest.json create mode 100644 homeassistant/components/stride/manifest.json create mode 100644 homeassistant/components/sun/manifest.json create mode 100644 homeassistant/components/supervisord/manifest.json create mode 100644 homeassistant/components/swiss_hydrological_data/manifest.json create mode 100644 homeassistant/components/swiss_public_transport/manifest.json create mode 100644 homeassistant/components/swisscom/manifest.json create mode 100644 homeassistant/components/switch/manifest.json create mode 100644 homeassistant/components/switchbot/manifest.json create mode 100644 homeassistant/components/switchmate/manifest.json create mode 100644 homeassistant/components/syncthru/manifest.json create mode 100644 homeassistant/components/synology/manifest.json create mode 100644 homeassistant/components/synology_chat/manifest.json create mode 100644 homeassistant/components/synology_srm/manifest.json create mode 100644 homeassistant/components/synologydsm/manifest.json create mode 100644 homeassistant/components/syslog/manifest.json create mode 100644 homeassistant/components/system_health/manifest.json create mode 100644 homeassistant/components/system_log/manifest.json create mode 100644 homeassistant/components/systemmonitor/manifest.json create mode 100644 homeassistant/components/sytadin/manifest.json create mode 100644 homeassistant/components/tado/manifest.json create mode 100644 homeassistant/components/tahoma/manifest.json create mode 100644 homeassistant/components/tank_utility/manifest.json create mode 100644 homeassistant/components/tapsaff/manifest.json create mode 100644 homeassistant/components/tautulli/manifest.json create mode 100644 homeassistant/components/tcp/manifest.json create mode 100644 homeassistant/components/ted5000/manifest.json create mode 100644 homeassistant/components/teksavvy/manifest.json create mode 100644 homeassistant/components/telegram/manifest.json create mode 100644 homeassistant/components/telegram_bot/manifest.json create mode 100644 homeassistant/components/tellduslive/manifest.json create mode 100644 homeassistant/components/tellstick/manifest.json create mode 100644 homeassistant/components/telnet/manifest.json create mode 100644 homeassistant/components/temper/manifest.json create mode 100644 homeassistant/components/template/manifest.json create mode 100644 homeassistant/components/tensorflow/manifest.json create mode 100644 homeassistant/components/tesla/manifest.json create mode 100644 homeassistant/components/tfiac/manifest.json create mode 100644 homeassistant/components/thermoworks_smoke/manifest.json create mode 100644 homeassistant/components/thethingsnetwork/manifest.json create mode 100644 homeassistant/components/thingspeak/manifest.json create mode 100644 homeassistant/components/thinkingcleaner/manifest.json create mode 100644 homeassistant/components/thomson/manifest.json create mode 100644 homeassistant/components/threshold/manifest.json create mode 100644 homeassistant/components/tibber/manifest.json create mode 100644 homeassistant/components/tikteck/manifest.json create mode 100644 homeassistant/components/tile/manifest.json create mode 100644 homeassistant/components/time_date/manifest.json create mode 100644 homeassistant/components/timer/manifest.json create mode 100644 homeassistant/components/tod/manifest.json create mode 100644 homeassistant/components/todoist/manifest.json create mode 100644 homeassistant/components/tof/manifest.json create mode 100644 homeassistant/components/tomato/manifest.json create mode 100644 homeassistant/components/toon/manifest.json create mode 100644 homeassistant/components/torque/manifest.json create mode 100644 homeassistant/components/totalconnect/manifest.json create mode 100644 homeassistant/components/touchline/manifest.json create mode 100644 homeassistant/components/tplink/manifest.json create mode 100644 homeassistant/components/tplink_lte/manifest.json create mode 100644 homeassistant/components/traccar/manifest.json create mode 100644 homeassistant/components/trackr/manifest.json create mode 100644 homeassistant/components/tradfri/manifest.json create mode 100644 homeassistant/components/trafikverket_weatherstation/manifest.json create mode 100644 homeassistant/components/transmission/manifest.json create mode 100644 homeassistant/components/transport_nsw/manifest.json create mode 100644 homeassistant/components/travisci/manifest.json create mode 100644 homeassistant/components/trend/manifest.json create mode 100644 homeassistant/components/tts/manifest.json create mode 100644 homeassistant/components/tuya/manifest.json create mode 100644 homeassistant/components/twilio/manifest.json create mode 100644 homeassistant/components/twilio_call/manifest.json create mode 100644 homeassistant/components/twilio_sms/manifest.json create mode 100644 homeassistant/components/twitch/manifest.json create mode 100644 homeassistant/components/twitter/manifest.json create mode 100644 homeassistant/components/ubee/manifest.json create mode 100644 homeassistant/components/uber/manifest.json create mode 100644 homeassistant/components/ubus/manifest.json create mode 100644 homeassistant/components/ue_smart_radio/manifest.json create mode 100644 homeassistant/components/uk_transport/manifest.json create mode 100644 homeassistant/components/unifi/manifest.json create mode 100644 homeassistant/components/unifi_direct/manifest.json create mode 100644 homeassistant/components/universal/manifest.json create mode 100644 homeassistant/components/upc_connect/manifest.json create mode 100644 homeassistant/components/upcloud/manifest.json create mode 100644 homeassistant/components/updater/manifest.json create mode 100644 homeassistant/components/upnp/manifest.json create mode 100644 homeassistant/components/ups/manifest.json create mode 100644 homeassistant/components/uptime/manifest.json create mode 100644 homeassistant/components/uptimerobot/manifest.json create mode 100644 homeassistant/components/uscis/manifest.json create mode 100644 homeassistant/components/usgs_earthquakes_feed/manifest.json create mode 100644 homeassistant/components/usps/manifest.json create mode 100644 homeassistant/components/utility_meter/manifest.json create mode 100644 homeassistant/components/uvc/manifest.json create mode 100644 homeassistant/components/vacuum/manifest.json create mode 100644 homeassistant/components/vasttrafik/manifest.json create mode 100644 homeassistant/components/velbus/manifest.json create mode 100644 homeassistant/components/velux/manifest.json create mode 100644 homeassistant/components/venstar/manifest.json create mode 100644 homeassistant/components/vera/manifest.json create mode 100644 homeassistant/components/verisure/manifest.json create mode 100644 homeassistant/components/version/manifest.json create mode 100644 homeassistant/components/vesync/manifest.json create mode 100644 homeassistant/components/viaggiatreno/manifest.json create mode 100644 homeassistant/components/vizio/manifest.json create mode 100644 homeassistant/components/vlc/manifest.json create mode 100644 homeassistant/components/voicerss/manifest.json create mode 100644 homeassistant/components/volkszaehler/manifest.json create mode 100644 homeassistant/components/volumio/manifest.json create mode 100644 homeassistant/components/volvooncall/manifest.json create mode 100644 homeassistant/components/vultr/manifest.json create mode 100644 homeassistant/components/w800rf32/manifest.json create mode 100644 homeassistant/components/wake_on_lan/manifest.json create mode 100644 homeassistant/components/waqi/manifest.json create mode 100644 homeassistant/components/water_heater/manifest.json create mode 100644 homeassistant/components/waterfurnace/manifest.json create mode 100644 homeassistant/components/watson_iot/manifest.json create mode 100644 homeassistant/components/waze_travel_time/manifest.json create mode 100644 homeassistant/components/weather/manifest.json create mode 100644 homeassistant/components/webhook/manifest.json create mode 100644 homeassistant/components/weblink/manifest.json create mode 100644 homeassistant/components/webostv/manifest.json create mode 100644 homeassistant/components/websocket_api/manifest.json create mode 100644 homeassistant/components/wemo/manifest.json create mode 100644 homeassistant/components/whois/manifest.json create mode 100644 homeassistant/components/wink/manifest.json create mode 100644 homeassistant/components/wirelesstag/manifest.json create mode 100644 homeassistant/components/workday/manifest.json create mode 100644 homeassistant/components/worldclock/manifest.json create mode 100644 homeassistant/components/worldtidesinfo/manifest.json create mode 100644 homeassistant/components/worxlandroid/manifest.json create mode 100644 homeassistant/components/wsdot/manifest.json create mode 100644 homeassistant/components/wunderground/manifest.json create mode 100644 homeassistant/components/wunderlist/manifest.json create mode 100644 homeassistant/components/x10/manifest.json create mode 100644 homeassistant/components/xbox_live/manifest.json create mode 100644 homeassistant/components/xeoma/manifest.json create mode 100644 homeassistant/components/xfinity/manifest.json create mode 100644 homeassistant/components/xiaomi/manifest.json create mode 100644 homeassistant/components/xiaomi_aqara/manifest.json create mode 100644 homeassistant/components/xiaomi_miio/manifest.json create mode 100644 homeassistant/components/xiaomi_tv/manifest.json create mode 100644 homeassistant/components/xmpp/manifest.json create mode 100644 homeassistant/components/xs1/manifest.json create mode 100644 homeassistant/components/yale_smart_alarm/manifest.json create mode 100644 homeassistant/components/yamaha/manifest.json create mode 100644 homeassistant/components/yamaha_musiccast/manifest.json create mode 100644 homeassistant/components/yandextts/manifest.json create mode 100644 homeassistant/components/yeelight/manifest.json create mode 100644 homeassistant/components/yeelightsunflower/manifest.json create mode 100644 homeassistant/components/yessssms/manifest.json create mode 100644 homeassistant/components/yi/manifest.json create mode 100644 homeassistant/components/yr/manifest.json create mode 100644 homeassistant/components/yweather/manifest.json create mode 100644 homeassistant/components/zabbix/manifest.json create mode 100644 homeassistant/components/zamg/manifest.json create mode 100644 homeassistant/components/zengge/manifest.json create mode 100644 homeassistant/components/zeroconf/manifest.json create mode 100644 homeassistant/components/zestimate/manifest.json create mode 100644 homeassistant/components/zha/manifest.json create mode 100644 homeassistant/components/zhong_hong/manifest.json create mode 100644 homeassistant/components/zigbee/manifest.json create mode 100644 homeassistant/components/ziggo_mediabox_xl/manifest.json create mode 100644 homeassistant/components/zone/manifest.json create mode 100644 homeassistant/components/zoneminder/manifest.json create mode 100644 homeassistant/components/zwave/manifest.json diff --git a/CODEOWNERS b/CODEOWNERS index 276c730a47a19a..5be5610a5c7ff5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,7 +18,11 @@ homeassistant/components/frontend/* @home-assistant/core homeassistant/components/group/* @home-assistant/core homeassistant/components/history/* @home-assistant/core homeassistant/components/http/* @home-assistant/core -homeassistant/components/input_*/* @home-assistant/core +homeassistant/components/input_boolean/* @home-assistant/core +homeassistant/components/input_datetime/* @home-assistant/core +homeassistant/components/input_number/* @home-assistant/core +homeassistant/components/input_select/* @home-assistant/core +homeassistant/components/input_text/* @home-assistant/core homeassistant/components/introduction/* @home-assistant/core homeassistant/components/logger/* @home-assistant/core homeassistant/components/lovelace/* @home-assistant/core @@ -42,7 +46,6 @@ Dockerfile @home-assistant/docker virtualization/Docker/* @home-assistant/docker homeassistant/components/zwave/* @home-assistant/z-wave -homeassistant/components/*/zwave.py @home-assistant/z-wave homeassistant/components/hassio/* @home-assistant/hassio @@ -223,7 +226,8 @@ homeassistant/components/spaceapi/* @fabaff homeassistant/components/spider/* @peternijssen homeassistant/components/sql/sensor.py @dgomes homeassistant/components/statistics/sensor.py @fabaff -homeassistant/components/swiss_*/* @fabaff +homeassistant/components/swiss_public_transport/* @fabaff +homeassistant/components/swiss_hydrological_data/* @fabaff homeassistant/components/switchbot/switch.py @danielhiversen homeassistant/components/switchmate/switch.py @danielhiversen homeassistant/components/synology_srm/device_tracker.py @aerialls diff --git a/homeassistant/components/abode/manifest.json b/homeassistant/components/abode/manifest.json new file mode 100644 index 00000000000000..49e0c46fd553ba --- /dev/null +++ b/homeassistant/components/abode/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "abode", + "name": "Abode", + "documentation": "https://www.home-assistant.io/components/abode", + "requirements": [ + "abodepy==0.15.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/acer_projector/manifest.json b/homeassistant/components/acer_projector/manifest.json new file mode 100644 index 00000000000000..4b8d6967491571 --- /dev/null +++ b/homeassistant/components/acer_projector/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "acer_projector", + "name": "Acer projector", + "documentation": "https://www.home-assistant.io/components/acer_projector", + "requirements": [ + "pyserial==3.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/actiontec/manifest.json b/homeassistant/components/actiontec/manifest.json new file mode 100644 index 00000000000000..e233f430cfcbbb --- /dev/null +++ b/homeassistant/components/actiontec/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "actiontec", + "name": "Actiontec", + "documentation": "https://www.home-assistant.io/components/actiontec", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ads/manifest.json b/homeassistant/components/ads/manifest.json new file mode 100644 index 00000000000000..0c759f0ad60c57 --- /dev/null +++ b/homeassistant/components/ads/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ads", + "name": "Ads", + "documentation": "https://www.home-assistant.io/components/ads", + "requirements": [ + "pyads==3.0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aftership/manifest.json b/homeassistant/components/aftership/manifest.json new file mode 100644 index 00000000000000..b9ee8939dc4631 --- /dev/null +++ b/homeassistant/components/aftership/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aftership", + "name": "Aftership", + "documentation": "https://www.home-assistant.io/components/aftership", + "requirements": [ + "pyaftership==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/air_quality/manifest.json b/homeassistant/components/air_quality/manifest.json new file mode 100644 index 00000000000000..5bfe85547ffce4 --- /dev/null +++ b/homeassistant/components/air_quality/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "air_quality", + "name": "Air quality", + "documentation": "https://www.home-assistant.io/components/air_quality", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/airvisual/manifest.json b/homeassistant/components/airvisual/manifest.json new file mode 100644 index 00000000000000..ddb109a99b07e5 --- /dev/null +++ b/homeassistant/components/airvisual/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "airvisual", + "name": "Airvisual", + "documentation": "https://www.home-assistant.io/components/airvisual", + "requirements": [ + "pyairvisual==3.0.1" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json new file mode 100644 index 00000000000000..0681d5df38b248 --- /dev/null +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aladdin_connect", + "name": "Aladdin connect", + "documentation": "https://www.home-assistant.io/components/aladdin_connect", + "requirements": [ + "aladdin_connect==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/alarm_control_panel/manifest.json b/homeassistant/components/alarm_control_panel/manifest.json new file mode 100644 index 00000000000000..95e26de53bcb3b --- /dev/null +++ b/homeassistant/components/alarm_control_panel/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "alarm_control_panel", + "name": "Alarm control panel", + "documentation": "https://www.home-assistant.io/components/alarm_control_panel", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@colinodell" + ] +} diff --git a/homeassistant/components/alarmdecoder/manifest.json b/homeassistant/components/alarmdecoder/manifest.json new file mode 100644 index 00000000000000..3e0d4112d2735f --- /dev/null +++ b/homeassistant/components/alarmdecoder/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "alarmdecoder", + "name": "Alarmdecoder", + "documentation": "https://www.home-assistant.io/components/alarmdecoder", + "requirements": [ + "alarmdecoder==1.13.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/alarmdotcom/manifest.json b/homeassistant/components/alarmdotcom/manifest.json new file mode 100644 index 00000000000000..9d2c0a2056e362 --- /dev/null +++ b/homeassistant/components/alarmdotcom/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "alarmdotcom", + "name": "Alarmdotcom", + "documentation": "https://www.home-assistant.io/components/alarmdotcom", + "requirements": [ + "pyalarmdotcom==0.3.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/alert/manifest.json b/homeassistant/components/alert/manifest.json new file mode 100644 index 00000000000000..f3dcc18208c368 --- /dev/null +++ b/homeassistant/components/alert/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "alert", + "name": "Alert", + "documentation": "https://www.home-assistant.io/components/alert", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/alexa/manifest.json b/homeassistant/components/alexa/manifest.json new file mode 100644 index 00000000000000..e4fc9eb86805aa --- /dev/null +++ b/homeassistant/components/alexa/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "alexa", + "name": "Alexa", + "documentation": "https://www.home-assistant.io/components/alexa", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/alpha_vantage/manifest.json b/homeassistant/components/alpha_vantage/manifest.json new file mode 100644 index 00000000000000..dacc428ea2ee36 --- /dev/null +++ b/homeassistant/components/alpha_vantage/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "alpha_vantage", + "name": "Alpha vantage", + "documentation": "https://www.home-assistant.io/components/alpha_vantage", + "requirements": [ + "alpha_vantage==2.1.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/amazon_polly/manifest.json b/homeassistant/components/amazon_polly/manifest.json new file mode 100644 index 00000000000000..19140aac939682 --- /dev/null +++ b/homeassistant/components/amazon_polly/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "amazon_polly", + "name": "Amazon polly", + "documentation": "https://www.home-assistant.io/components/amazon_polly", + "requirements": [ + "boto3==1.9.16" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/ambient_station/manifest.json b/homeassistant/components/ambient_station/manifest.json new file mode 100644 index 00000000000000..13a74fec26e2e2 --- /dev/null +++ b/homeassistant/components/ambient_station/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ambient_station", + "name": "Ambient station", + "documentation": "https://www.home-assistant.io/components/ambient_station", + "requirements": [ + "aioambient==0.1.3" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/amcrest/manifest.json b/homeassistant/components/amcrest/manifest.json new file mode 100644 index 00000000000000..e05fdcf4bd4dea --- /dev/null +++ b/homeassistant/components/amcrest/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "amcrest", + "name": "Amcrest", + "documentation": "https://www.home-assistant.io/components/amcrest", + "requirements": [ + "amcrest==1.3.0" + ], + "dependencies": [ + "ffmpeg" + ], + "codeowners": [] +} diff --git a/homeassistant/components/ampio/manifest.json b/homeassistant/components/ampio/manifest.json new file mode 100644 index 00000000000000..d20b10b2d1509e --- /dev/null +++ b/homeassistant/components/ampio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ampio", + "name": "Ampio", + "documentation": "https://www.home-assistant.io/components/ampio", + "requirements": [ + "asmog==0.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/android_ip_webcam/manifest.json b/homeassistant/components/android_ip_webcam/manifest.json new file mode 100644 index 00000000000000..28909f7e053379 --- /dev/null +++ b/homeassistant/components/android_ip_webcam/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "android_ip_webcam", + "name": "Android ip webcam", + "documentation": "https://www.home-assistant.io/components/android_ip_webcam", + "requirements": [ + "pydroid-ipcam==0.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/androidtv/manifest.json b/homeassistant/components/androidtv/manifest.json new file mode 100644 index 00000000000000..815a97394cb505 --- /dev/null +++ b/homeassistant/components/androidtv/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "androidtv", + "name": "Androidtv", + "documentation": "https://www.home-assistant.io/components/androidtv", + "requirements": [ + "androidtv==0.0.14" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/anel_pwrctrl/manifest.json b/homeassistant/components/anel_pwrctrl/manifest.json new file mode 100644 index 00000000000000..17802918cd2263 --- /dev/null +++ b/homeassistant/components/anel_pwrctrl/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "anel_pwrctrl", + "name": "Anel pwrctrl", + "documentation": "https://www.home-assistant.io/components/anel_pwrctrl", + "requirements": [ + "anel_pwrctrl-homeassistant==0.0.1.dev2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/anthemav/manifest.json b/homeassistant/components/anthemav/manifest.json new file mode 100644 index 00000000000000..9b2e3c697bb220 --- /dev/null +++ b/homeassistant/components/anthemav/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "anthemav", + "name": "Anthemav", + "documentation": "https://www.home-assistant.io/components/anthemav", + "requirements": [ + "anthemav==1.1.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/apcupsd/manifest.json b/homeassistant/components/apcupsd/manifest.json new file mode 100644 index 00000000000000..813176728f2843 --- /dev/null +++ b/homeassistant/components/apcupsd/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "apcupsd", + "name": "Apcupsd", + "documentation": "https://www.home-assistant.io/components/apcupsd", + "requirements": [ + "apcaccess==0.0.13" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/api/manifest.json b/homeassistant/components/api/manifest.json new file mode 100644 index 00000000000000..25d9a76036eec6 --- /dev/null +++ b/homeassistant/components/api/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "api", + "name": "Home Assistant API", + "documentation": "https://www.home-assistant.io/components/api", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/api_streams/__init__.py b/homeassistant/components/api_streams/__init__.py deleted file mode 100644 index dba4306131386c..00000000000000 --- a/homeassistant/components/api_streams/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The api_streams component.""" diff --git a/homeassistant/components/apns/manifest.json b/homeassistant/components/apns/manifest.json new file mode 100644 index 00000000000000..9a310a096a5a4d --- /dev/null +++ b/homeassistant/components/apns/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "apns", + "name": "Apns", + "documentation": "https://www.home-assistant.io/components/apns", + "requirements": [ + "apns2==0.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/apple_tv/manifest.json b/homeassistant/components/apple_tv/manifest.json new file mode 100644 index 00000000000000..4f27fde2aa324a --- /dev/null +++ b/homeassistant/components/apple_tv/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "apple_tv", + "name": "Apple tv", + "documentation": "https://www.home-assistant.io/components/apple_tv", + "requirements": [ + "pyatv==0.3.12" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aqualogic/manifest.json b/homeassistant/components/aqualogic/manifest.json new file mode 100644 index 00000000000000..40f1805d83abec --- /dev/null +++ b/homeassistant/components/aqualogic/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aqualogic", + "name": "Aqualogic", + "documentation": "https://www.home-assistant.io/components/aqualogic", + "requirements": [ + "aqualogic==1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aquostv/manifest.json b/homeassistant/components/aquostv/manifest.json new file mode 100644 index 00000000000000..16865905ae984a --- /dev/null +++ b/homeassistant/components/aquostv/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aquostv", + "name": "Aquostv", + "documentation": "https://www.home-assistant.io/components/aquostv", + "requirements": [ + "sharp_aquos_rc==0.3.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/arduino/manifest.json b/homeassistant/components/arduino/manifest.json new file mode 100644 index 00000000000000..cf21cbe87eafe2 --- /dev/null +++ b/homeassistant/components/arduino/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "arduino", + "name": "Arduino", + "documentation": "https://www.home-assistant.io/components/arduino", + "requirements": [ + "PyMata==2.14" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/arest/manifest.json b/homeassistant/components/arest/manifest.json new file mode 100644 index 00000000000000..d5bcf92a39dc4b --- /dev/null +++ b/homeassistant/components/arest/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "arest", + "name": "Arest", + "documentation": "https://www.home-assistant.io/components/arest", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/arlo/manifest.json b/homeassistant/components/arlo/manifest.json new file mode 100644 index 00000000000000..a8b6befb70fa45 --- /dev/null +++ b/homeassistant/components/arlo/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "arlo", + "name": "Arlo", + "documentation": "https://www.home-assistant.io/components/arlo", + "requirements": [ + "pyarlo==0.2.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aruba/manifest.json b/homeassistant/components/aruba/manifest.json new file mode 100644 index 00000000000000..597975619e6a4e --- /dev/null +++ b/homeassistant/components/aruba/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aruba", + "name": "Aruba", + "documentation": "https://www.home-assistant.io/components/aruba", + "requirements": [ + "pexpect==4.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/arwn/manifest.json b/homeassistant/components/arwn/manifest.json new file mode 100644 index 00000000000000..15ef7fa48ba8fe --- /dev/null +++ b/homeassistant/components/arwn/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "arwn", + "name": "Arwn", + "documentation": "https://www.home-assistant.io/components/arwn", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/asterisk_cdr/manifest.json b/homeassistant/components/asterisk_cdr/manifest.json new file mode 100644 index 00000000000000..2c8713ac191b83 --- /dev/null +++ b/homeassistant/components/asterisk_cdr/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "asterisk_cdr", + "name": "Asterisk cdr", + "documentation": "https://www.home-assistant.io/components/asterisk_cdr", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/asterisk_mbox/manifest.json b/homeassistant/components/asterisk_mbox/manifest.json new file mode 100644 index 00000000000000..bafe43c480f4f7 --- /dev/null +++ b/homeassistant/components/asterisk_mbox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "asterisk_mbox", + "name": "Asterisk mbox", + "documentation": "https://www.home-assistant.io/components/asterisk_mbox", + "requirements": [ + "asterisk_mbox==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/asuswrt/manifest.json b/homeassistant/components/asuswrt/manifest.json new file mode 100644 index 00000000000000..f36819f133ddb8 --- /dev/null +++ b/homeassistant/components/asuswrt/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "asuswrt", + "name": "Asuswrt", + "documentation": "https://www.home-assistant.io/components/asuswrt", + "requirements": [ + "aioasuswrt==1.1.21" + ], + "dependencies": [], + "codeowners": [ + "@kennedyshead" + ] +} diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json new file mode 100644 index 00000000000000..39bc70fba7bf71 --- /dev/null +++ b/homeassistant/components/august/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "august", + "name": "August", + "documentation": "https://www.home-assistant.io/components/august", + "requirements": [ + "py-august==0.7.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aurora/manifest.json b/homeassistant/components/aurora/manifest.json new file mode 100644 index 00000000000000..56ba3fe935608a --- /dev/null +++ b/homeassistant/components/aurora/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "aurora", + "name": "Aurora", + "documentation": "https://www.home-assistant.io/components/aurora", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/auth/manifest.json b/homeassistant/components/auth/manifest.json new file mode 100644 index 00000000000000..10be545f5e14eb --- /dev/null +++ b/homeassistant/components/auth/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "auth", + "name": "Auth", + "documentation": "https://www.home-assistant.io/components/auth", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/automatic/manifest.json b/homeassistant/components/automatic/manifest.json new file mode 100644 index 00000000000000..db2f676813eea5 --- /dev/null +++ b/homeassistant/components/automatic/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "automatic", + "name": "Automatic", + "documentation": "https://www.home-assistant.io/components/automatic", + "requirements": [ + "aioautomatic==0.6.5" + ], + "dependencies": [], + "codeowners": [ + "@armills" + ] +} diff --git a/homeassistant/components/automation/manifest.json b/homeassistant/components/automation/manifest.json new file mode 100644 index 00000000000000..93f1abe0f0d894 --- /dev/null +++ b/homeassistant/components/automation/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "automation", + "name": "Automation", + "documentation": "https://www.home-assistant.io/components/automation", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/avion/manifest.json b/homeassistant/components/avion/manifest.json new file mode 100644 index 00000000000000..e7d97f13313088 --- /dev/null +++ b/homeassistant/components/avion/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "avion", + "name": "Avion", + "documentation": "https://www.home-assistant.io/components/avion", + "requirements": [ + "avion==0.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/awair/manifest.json b/homeassistant/components/awair/manifest.json new file mode 100644 index 00000000000000..bc63ef06cc2f5b --- /dev/null +++ b/homeassistant/components/awair/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "awair", + "name": "Awair", + "documentation": "https://www.home-assistant.io/components/awair", + "requirements": [ + "python_awair==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aws/manifest.json b/homeassistant/components/aws/manifest.json new file mode 100644 index 00000000000000..a473a23f917ab0 --- /dev/null +++ b/homeassistant/components/aws/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "aws", + "name": "Aws", + "documentation": "https://www.home-assistant.io/components/aws", + "requirements": [ + "aiobotocore==0.10.2" + ], + "dependencies": [], + "codeowners": [ + "@awarecan", + "@robbiet480" + ] +} diff --git a/homeassistant/components/aws_lambda/manifest.json b/homeassistant/components/aws_lambda/manifest.json new file mode 100644 index 00000000000000..40c8c7b06290fa --- /dev/null +++ b/homeassistant/components/aws_lambda/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aws_lambda", + "name": "Aws lambda", + "documentation": "https://www.home-assistant.io/components/aws_lambda", + "requirements": [ + "boto3==1.9.16" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aws_sns/manifest.json b/homeassistant/components/aws_sns/manifest.json new file mode 100644 index 00000000000000..f6c3438025d097 --- /dev/null +++ b/homeassistant/components/aws_sns/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aws_sns", + "name": "Aws sns", + "documentation": "https://www.home-assistant.io/components/aws_sns", + "requirements": [ + "boto3==1.9.16" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/aws_sqs/manifest.json b/homeassistant/components/aws_sqs/manifest.json new file mode 100644 index 00000000000000..fcfc8cfb2976bb --- /dev/null +++ b/homeassistant/components/aws_sqs/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "aws_sqs", + "name": "Aws sqs", + "documentation": "https://www.home-assistant.io/components/aws_sqs", + "requirements": [ + "boto3==1.9.16" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/axis/manifest.json b/homeassistant/components/axis/manifest.json new file mode 100644 index 00000000000000..66ccce8d98fbf0 --- /dev/null +++ b/homeassistant/components/axis/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "axis", + "name": "Axis", + "documentation": "https://www.home-assistant.io/components/axis", + "requirements": [ + "axis==19" + ], + "dependencies": [], + "codeowners": [ + "@kane610" + ] +} diff --git a/homeassistant/components/baidu/manifest.json b/homeassistant/components/baidu/manifest.json new file mode 100644 index 00000000000000..1dea1b7e37b147 --- /dev/null +++ b/homeassistant/components/baidu/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "baidu", + "name": "Baidu", + "documentation": "https://www.home-assistant.io/components/baidu", + "requirements": [ + "baidu-aip==1.6.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bayesian/manifest.json b/homeassistant/components/bayesian/manifest.json new file mode 100644 index 00000000000000..25480ac8bdc870 --- /dev/null +++ b/homeassistant/components/bayesian/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "bayesian", + "name": "Bayesian", + "documentation": "https://www.home-assistant.io/components/bayesian", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bbb_gpio/manifest.json b/homeassistant/components/bbb_gpio/manifest.json new file mode 100644 index 00000000000000..5632836bfbb623 --- /dev/null +++ b/homeassistant/components/bbb_gpio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "bbb_gpio", + "name": "Bbb gpio", + "documentation": "https://www.home-assistant.io/components/bbb_gpio", + "requirements": [ + "Adafruit_BBIO==1.0.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bbox/manifest.json b/homeassistant/components/bbox/manifest.json new file mode 100644 index 00000000000000..54cd9a3af64dda --- /dev/null +++ b/homeassistant/components/bbox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "bbox", + "name": "Bbox", + "documentation": "https://www.home-assistant.io/components/bbox", + "requirements": [ + "pybbox==0.0.5-alpha" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bh1750/manifest.json b/homeassistant/components/bh1750/manifest.json new file mode 100644 index 00000000000000..90e62c783569cb --- /dev/null +++ b/homeassistant/components/bh1750/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "bh1750", + "name": "Bh1750", + "documentation": "https://www.home-assistant.io/components/bh1750", + "requirements": [ + "i2csense==0.0.4", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/binary_sensor/manifest.json b/homeassistant/components/binary_sensor/manifest.json new file mode 100644 index 00000000000000..d627351958d573 --- /dev/null +++ b/homeassistant/components/binary_sensor/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "binary_sensor", + "name": "Binary sensor", + "documentation": "https://www.home-assistant.io/components/binary_sensor", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bitcoin/manifest.json b/homeassistant/components/bitcoin/manifest.json new file mode 100644 index 00000000000000..85da99a68850cf --- /dev/null +++ b/homeassistant/components/bitcoin/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "bitcoin", + "name": "Bitcoin", + "documentation": "https://www.home-assistant.io/components/bitcoin", + "requirements": [ + "blockchain==1.4.4" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/blackbird/manifest.json b/homeassistant/components/blackbird/manifest.json new file mode 100644 index 00000000000000..9e3e41290ea373 --- /dev/null +++ b/homeassistant/components/blackbird/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "blackbird", + "name": "Blackbird", + "documentation": "https://www.home-assistant.io/components/blackbird", + "requirements": [ + "pyblackbird==0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/blink/manifest.json b/homeassistant/components/blink/manifest.json new file mode 100644 index 00000000000000..7be44f95a5335c --- /dev/null +++ b/homeassistant/components/blink/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "blink", + "name": "Blink", + "documentation": "https://www.home-assistant.io/components/blink", + "requirements": [ + "blinkpy==0.13.1" + ], + "dependencies": [], + "codeowners": [ + "@fronzbot" + ] +} diff --git a/homeassistant/components/blinksticklight/manifest.json b/homeassistant/components/blinksticklight/manifest.json new file mode 100644 index 00000000000000..a5277c97d99386 --- /dev/null +++ b/homeassistant/components/blinksticklight/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "blinksticklight", + "name": "Blinksticklight", + "documentation": "https://www.home-assistant.io/components/blinksticklight", + "requirements": [ + "blinkstick==1.1.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/blinkt/manifest.json b/homeassistant/components/blinkt/manifest.json new file mode 100644 index 00000000000000..c11583ed59ec3d --- /dev/null +++ b/homeassistant/components/blinkt/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "blinkt", + "name": "Blinkt", + "documentation": "https://www.home-assistant.io/components/blinkt", + "requirements": [ + "blinkt==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/blockchain/manifest.json b/homeassistant/components/blockchain/manifest.json new file mode 100644 index 00000000000000..8a2a9f7b71f00d --- /dev/null +++ b/homeassistant/components/blockchain/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "blockchain", + "name": "Blockchain", + "documentation": "https://www.home-assistant.io/components/blockchain", + "requirements": [ + "python-blockchain-api==0.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bloomsky/manifest.json b/homeassistant/components/bloomsky/manifest.json new file mode 100644 index 00000000000000..3a780507dd59c4 --- /dev/null +++ b/homeassistant/components/bloomsky/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "bloomsky", + "name": "Bloomsky", + "documentation": "https://www.home-assistant.io/components/bloomsky", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bluesound/manifest.json b/homeassistant/components/bluesound/manifest.json new file mode 100644 index 00000000000000..9016502b5d3bb0 --- /dev/null +++ b/homeassistant/components/bluesound/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "bluesound", + "name": "Bluesound", + "documentation": "https://www.home-assistant.io/components/bluesound", + "requirements": [ + "xmltodict==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bluetooth_le_tracker/manifest.json b/homeassistant/components/bluetooth_le_tracker/manifest.json new file mode 100644 index 00000000000000..cd67ec31536db5 --- /dev/null +++ b/homeassistant/components/bluetooth_le_tracker/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "bluetooth_le_tracker", + "name": "Bluetooth le tracker", + "documentation": "https://www.home-assistant.io/components/bluetooth_le_tracker", + "requirements": [ + "pygatt[GATTTOOL]==3.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bluetooth_tracker/manifest.json b/homeassistant/components/bluetooth_tracker/manifest.json new file mode 100644 index 00000000000000..7eaeb4ef927485 --- /dev/null +++ b/homeassistant/components/bluetooth_tracker/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "bluetooth_tracker", + "name": "Bluetooth tracker", + "documentation": "https://www.home-assistant.io/components/bluetooth_tracker", + "requirements": [ + "bt_proximity==0.1.2", + "pybluez==0.22" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bme280/manifest.json b/homeassistant/components/bme280/manifest.json new file mode 100644 index 00000000000000..2342c8418ebce2 --- /dev/null +++ b/homeassistant/components/bme280/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "bme280", + "name": "Bme280", + "documentation": "https://www.home-assistant.io/components/bme280", + "requirements": [ + "i2csense==0.0.4", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bme680/manifest.json b/homeassistant/components/bme680/manifest.json new file mode 100644 index 00000000000000..976be85ca9413e --- /dev/null +++ b/homeassistant/components/bme680/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "bme680", + "name": "Bme680", + "documentation": "https://www.home-assistant.io/components/bme680", + "requirements": [ + "bme680==1.0.5", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json new file mode 100644 index 00000000000000..67bfac9105203e --- /dev/null +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "bmw_connected_drive", + "name": "Bmw connected drive", + "documentation": "https://www.home-assistant.io/components/bmw_connected_drive", + "requirements": [ + "bimmer_connected==0.5.3" + ], + "dependencies": [], + "codeowners": [ + "@ChristianKuehnel" + ] +} diff --git a/homeassistant/components/bom/manifest.json b/homeassistant/components/bom/manifest.json new file mode 100644 index 00000000000000..e4744d4cfd2a11 --- /dev/null +++ b/homeassistant/components/bom/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "bom", + "name": "Bom", + "documentation": "https://www.home-assistant.io/components/bom", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/braviatv/manifest.json b/homeassistant/components/braviatv/manifest.json new file mode 100644 index 00000000000000..35e2698af4d26d --- /dev/null +++ b/homeassistant/components/braviatv/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "braviatv", + "name": "Braviatv", + "documentation": "https://www.home-assistant.io/components/braviatv", + "requirements": [ + "braviarc-homeassistant==0.3.7.dev0" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/broadlink/manifest.json b/homeassistant/components/broadlink/manifest.json new file mode 100644 index 00000000000000..a2c565c3dd5cdd --- /dev/null +++ b/homeassistant/components/broadlink/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "broadlink", + "name": "Broadlink", + "documentation": "https://www.home-assistant.io/components/broadlink", + "requirements": [ + "broadlink==0.9.0" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/brottsplatskartan/manifest.json b/homeassistant/components/brottsplatskartan/manifest.json new file mode 100644 index 00000000000000..d3b0657fed82e8 --- /dev/null +++ b/homeassistant/components/brottsplatskartan/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "brottsplatskartan", + "name": "Brottsplatskartan", + "documentation": "https://www.home-assistant.io/components/brottsplatskartan", + "requirements": [ + "brottsplatskartan==0.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/browser/manifest.json b/homeassistant/components/browser/manifest.json new file mode 100644 index 00000000000000..61823564fe9180 --- /dev/null +++ b/homeassistant/components/browser/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "browser", + "name": "Browser", + "documentation": "https://www.home-assistant.io/components/browser", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/brunt/manifest.json b/homeassistant/components/brunt/manifest.json new file mode 100644 index 00000000000000..a47e3f69d5cf08 --- /dev/null +++ b/homeassistant/components/brunt/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "brunt", + "name": "Brunt", + "documentation": "https://www.home-assistant.io/components/brunt", + "requirements": [ + "brunt==0.1.3" + ], + "dependencies": [], + "codeowners": [ + "@eavanvalkenburg" + ] +} diff --git a/homeassistant/components/bt_home_hub_5/manifest.json b/homeassistant/components/bt_home_hub_5/manifest.json new file mode 100644 index 00000000000000..927d9ea941230b --- /dev/null +++ b/homeassistant/components/bt_home_hub_5/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "bt_home_hub_5", + "name": "Bt home hub 5", + "documentation": "https://www.home-assistant.io/components/bt_home_hub_5", + "requirements": [ + "bthomehub5-devicelist==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/bt_smarthub/manifest.json b/homeassistant/components/bt_smarthub/manifest.json new file mode 100644 index 00000000000000..725541082e701d --- /dev/null +++ b/homeassistant/components/bt_smarthub/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "bt_smarthub", + "name": "Bt smarthub", + "documentation": "https://www.home-assistant.io/components/bt_smarthub", + "requirements": [ + "btsmarthub_devicelist==0.1.3" + ], + "dependencies": [], + "codeowners": [ + "@jxwolstenholme" + ] +} diff --git a/homeassistant/components/buienradar/manifest.json b/homeassistant/components/buienradar/manifest.json new file mode 100644 index 00000000000000..98fc5fbdeac458 --- /dev/null +++ b/homeassistant/components/buienradar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "buienradar", + "name": "Buienradar", + "documentation": "https://www.home-assistant.io/components/buienradar", + "requirements": [ + "buienradar==0.91" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/caldav/manifest.json b/homeassistant/components/caldav/manifest.json new file mode 100644 index 00000000000000..6e233ba6ccd826 --- /dev/null +++ b/homeassistant/components/caldav/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "caldav", + "name": "Caldav", + "documentation": "https://www.home-assistant.io/components/caldav", + "requirements": [ + "caldav==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/calendar/manifest.json b/homeassistant/components/calendar/manifest.json new file mode 100644 index 00000000000000..3a09cd090a523d --- /dev/null +++ b/homeassistant/components/calendar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "calendar", + "name": "Calendar", + "documentation": "https://www.home-assistant.io/components/calendar", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/camera/manifest.json b/homeassistant/components/camera/manifest.json new file mode 100644 index 00000000000000..afa6f0d9bb7e6b --- /dev/null +++ b/homeassistant/components/camera/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "camera", + "name": "Camera", + "documentation": "https://www.home-assistant.io/components/camera", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/canary/manifest.json b/homeassistant/components/canary/manifest.json new file mode 100644 index 00000000000000..e7cc5fa7efc68a --- /dev/null +++ b/homeassistant/components/canary/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "canary", + "name": "Canary", + "documentation": "https://www.home-assistant.io/components/canary", + "requirements": [ + "py-canary==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json new file mode 100644 index 00000000000000..c506dba8cf1e66 --- /dev/null +++ b/homeassistant/components/cast/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cast", + "name": "Cast", + "documentation": "https://www.home-assistant.io/components/cast", + "requirements": [ + "pychromecast==3.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cert_expiry/manifest.json b/homeassistant/components/cert_expiry/manifest.json new file mode 100644 index 00000000000000..7ef2e0b7d105d6 --- /dev/null +++ b/homeassistant/components/cert_expiry/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "cert_expiry", + "name": "Cert expiry", + "documentation": "https://www.home-assistant.io/components/cert_expiry", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/channels/manifest.json b/homeassistant/components/channels/manifest.json new file mode 100644 index 00000000000000..152c7d3a2dc5eb --- /dev/null +++ b/homeassistant/components/channels/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "channels", + "name": "Channels", + "documentation": "https://www.home-assistant.io/components/channels", + "requirements": [ + "pychannels==1.0.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cisco_ios/manifest.json b/homeassistant/components/cisco_ios/manifest.json new file mode 100644 index 00000000000000..d1a9e9933b922e --- /dev/null +++ b/homeassistant/components/cisco_ios/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cisco_ios", + "name": "Cisco ios", + "documentation": "https://www.home-assistant.io/components/cisco_ios", + "requirements": [ + "pexpect==4.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cisco_mobility_express/manifest.json b/homeassistant/components/cisco_mobility_express/manifest.json new file mode 100644 index 00000000000000..6bd56ccd15e487 --- /dev/null +++ b/homeassistant/components/cisco_mobility_express/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cisco_mobility_express", + "name": "Cisco mobility express", + "documentation": "https://www.home-assistant.io/components/cisco_mobility_express", + "requirements": [ + "ciscomobilityexpress==0.1.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cisco_webex_teams/manifest.json b/homeassistant/components/cisco_webex_teams/manifest.json new file mode 100644 index 00000000000000..d13b893ce69913 --- /dev/null +++ b/homeassistant/components/cisco_webex_teams/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cisco_webex_teams", + "name": "Cisco webex teams", + "documentation": "https://www.home-assistant.io/components/cisco_webex_teams", + "requirements": [ + "webexteamssdk==1.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ciscospark/manifest.json b/homeassistant/components/ciscospark/manifest.json new file mode 100644 index 00000000000000..c6b0c42e89c5c6 --- /dev/null +++ b/homeassistant/components/ciscospark/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ciscospark", + "name": "Ciscospark", + "documentation": "https://www.home-assistant.io/components/ciscospark", + "requirements": [ + "ciscosparkapi==0.4.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/citybikes/manifest.json b/homeassistant/components/citybikes/manifest.json new file mode 100644 index 00000000000000..ea1ceaa9531a54 --- /dev/null +++ b/homeassistant/components/citybikes/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "citybikes", + "name": "Citybikes", + "documentation": "https://www.home-assistant.io/components/citybikes", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/clementine/manifest.json b/homeassistant/components/clementine/manifest.json new file mode 100644 index 00000000000000..4d835ed4e7c2d1 --- /dev/null +++ b/homeassistant/components/clementine/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "clementine", + "name": "Clementine", + "documentation": "https://www.home-assistant.io/components/clementine", + "requirements": [ + "python-clementine-remote==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/clickatell/manifest.json b/homeassistant/components/clickatell/manifest.json new file mode 100644 index 00000000000000..ffd550eebee871 --- /dev/null +++ b/homeassistant/components/clickatell/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "clickatell", + "name": "Clickatell", + "documentation": "https://www.home-assistant.io/components/clickatell", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/clicksend/manifest.json b/homeassistant/components/clicksend/manifest.json new file mode 100644 index 00000000000000..3831982509431e --- /dev/null +++ b/homeassistant/components/clicksend/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "clicksend", + "name": "Clicksend", + "documentation": "https://www.home-assistant.io/components/clicksend", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/clicksend_tts/manifest.json b/homeassistant/components/clicksend_tts/manifest.json new file mode 100644 index 00000000000000..c2a86f426e43e0 --- /dev/null +++ b/homeassistant/components/clicksend_tts/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "clicksend_tts", + "name": "Clicksend tts", + "documentation": "https://www.home-assistant.io/components/clicksend_tts", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/climate/manifest.json b/homeassistant/components/climate/manifest.json new file mode 100644 index 00000000000000..ca5312e7670b1c --- /dev/null +++ b/homeassistant/components/climate/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "climate", + "name": "Climate", + "documentation": "https://www.home-assistant.io/components/climate", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json new file mode 100644 index 00000000000000..b7822fcd90382e --- /dev/null +++ b/homeassistant/components/cloud/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "cloud", + "name": "Cloud", + "documentation": "https://www.home-assistant.io/components/cloud", + "requirements": [ + "hass-nabucasa==0.11" + ], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/cloudflare/manifest.json b/homeassistant/components/cloudflare/manifest.json new file mode 100644 index 00000000000000..7716ae65c4e1a1 --- /dev/null +++ b/homeassistant/components/cloudflare/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "cloudflare", + "name": "Cloudflare", + "documentation": "https://www.home-assistant.io/components/cloudflare", + "requirements": [ + "pycfdns==0.0.1" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/cmus/manifest.json b/homeassistant/components/cmus/manifest.json new file mode 100644 index 00000000000000..1528f4252b1098 --- /dev/null +++ b/homeassistant/components/cmus/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cmus", + "name": "Cmus", + "documentation": "https://www.home-assistant.io/components/cmus", + "requirements": [ + "pycmus==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/co2signal/manifest.json b/homeassistant/components/co2signal/manifest.json new file mode 100644 index 00000000000000..ac42e374fdd23e --- /dev/null +++ b/homeassistant/components/co2signal/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "co2signal", + "name": "Co2signal", + "documentation": "https://www.home-assistant.io/components/co2signal", + "requirements": [ + "co2signal==0.4.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/coinbase/manifest.json b/homeassistant/components/coinbase/manifest.json new file mode 100644 index 00000000000000..5f8a189c7d129f --- /dev/null +++ b/homeassistant/components/coinbase/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "coinbase", + "name": "Coinbase", + "documentation": "https://www.home-assistant.io/components/coinbase", + "requirements": [ + "coinbase==2.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/coinmarketcap/manifest.json b/homeassistant/components/coinmarketcap/manifest.json new file mode 100644 index 00000000000000..0afb1b1c28f317 --- /dev/null +++ b/homeassistant/components/coinmarketcap/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "coinmarketcap", + "name": "Coinmarketcap", + "documentation": "https://www.home-assistant.io/components/coinmarketcap", + "requirements": [ + "coinmarketcap==5.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/comed_hourly_pricing/manifest.json b/homeassistant/components/comed_hourly_pricing/manifest.json new file mode 100644 index 00000000000000..47c7931a0e93d2 --- /dev/null +++ b/homeassistant/components/comed_hourly_pricing/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "comed_hourly_pricing", + "name": "Comed hourly pricing", + "documentation": "https://www.home-assistant.io/components/comed_hourly_pricing", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/comfoconnect/manifest.json b/homeassistant/components/comfoconnect/manifest.json new file mode 100644 index 00000000000000..03319aeffa89b3 --- /dev/null +++ b/homeassistant/components/comfoconnect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "comfoconnect", + "name": "Comfoconnect", + "documentation": "https://www.home-assistant.io/components/comfoconnect", + "requirements": [ + "pycomfoconnect==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/command_line/manifest.json b/homeassistant/components/command_line/manifest.json new file mode 100644 index 00000000000000..ff94522210d818 --- /dev/null +++ b/homeassistant/components/command_line/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "command_line", + "name": "Command line", + "documentation": "https://www.home-assistant.io/components/command_line", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/concord232/manifest.json b/homeassistant/components/concord232/manifest.json new file mode 100644 index 00000000000000..f26da49d3f1b84 --- /dev/null +++ b/homeassistant/components/concord232/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "concord232", + "name": "Concord232", + "documentation": "https://www.home-assistant.io/components/concord232", + "requirements": [ + "concord232==0.15" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/config/manifest.json b/homeassistant/components/config/manifest.json new file mode 100644 index 00000000000000..9c0c50a25957ee --- /dev/null +++ b/homeassistant/components/config/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "config", + "name": "Config", + "documentation": "https://www.home-assistant.io/components/config", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/configurator/manifest.json b/homeassistant/components/configurator/manifest.json new file mode 100644 index 00000000000000..f01fe7324fa493 --- /dev/null +++ b/homeassistant/components/configurator/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "configurator", + "name": "Configurator", + "documentation": "https://www.home-assistant.io/components/configurator", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/conversation/manifest.json b/homeassistant/components/conversation/manifest.json new file mode 100644 index 00000000000000..ddd3b6205efdd4 --- /dev/null +++ b/homeassistant/components/conversation/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "conversation", + "name": "Conversation", + "documentation": "https://www.home-assistant.io/components/conversation", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/coolmaster/manifest.json b/homeassistant/components/coolmaster/manifest.json new file mode 100644 index 00000000000000..9489dc72689e5b --- /dev/null +++ b/homeassistant/components/coolmaster/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "coolmaster", + "name": "Coolmaster", + "documentation": "https://www.home-assistant.io/components/coolmaster", + "requirements": [ + "pycoolmasternet==0.0.4" + ], + "dependencies": [], + "codeowners": [ + "@OnFreund" + ] +} diff --git a/homeassistant/components/counter/manifest.json b/homeassistant/components/counter/manifest.json new file mode 100644 index 00000000000000..ae7066ea82d28d --- /dev/null +++ b/homeassistant/components/counter/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "counter", + "name": "Counter", + "documentation": "https://www.home-assistant.io/components/counter", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/cover/manifest.json b/homeassistant/components/cover/manifest.json new file mode 100644 index 00000000000000..f39f7fb0650645 --- /dev/null +++ b/homeassistant/components/cover/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "cover", + "name": "Cover", + "documentation": "https://www.home-assistant.io/components/cover", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [ + "@cdce8p" + ] +} diff --git a/homeassistant/components/cppm_tracker/manifest.json b/homeassistant/components/cppm_tracker/manifest.json new file mode 100644 index 00000000000000..5a1bdbf5a452ec --- /dev/null +++ b/homeassistant/components/cppm_tracker/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "cppm_tracker", + "name": "Cppm tracker", + "documentation": "https://www.home-assistant.io/components/cppm_tracker", + "requirements": [ + "clearpasspy==1.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cpuspeed/manifest.json b/homeassistant/components/cpuspeed/manifest.json new file mode 100644 index 00000000000000..9034cb7740d0f3 --- /dev/null +++ b/homeassistant/components/cpuspeed/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "cpuspeed", + "name": "Cpuspeed", + "documentation": "https://www.home-assistant.io/components/cpuspeed", + "requirements": [ + "py-cpuinfo==5.0.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/crimereports/manifest.json b/homeassistant/components/crimereports/manifest.json new file mode 100644 index 00000000000000..0f74216b9b2156 --- /dev/null +++ b/homeassistant/components/crimereports/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "crimereports", + "name": "Crimereports", + "documentation": "https://www.home-assistant.io/components/crimereports", + "requirements": [ + "crimereports==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/cups/manifest.json b/homeassistant/components/cups/manifest.json new file mode 100644 index 00000000000000..def2846c4ca3b6 --- /dev/null +++ b/homeassistant/components/cups/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "cups", + "name": "Cups", + "documentation": "https://www.home-assistant.io/components/cups", + "requirements": [ + "pycups==1.9.73" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/currencylayer/manifest.json b/homeassistant/components/currencylayer/manifest.json new file mode 100644 index 00000000000000..7064590bf25877 --- /dev/null +++ b/homeassistant/components/currencylayer/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "currencylayer", + "name": "Currencylayer", + "documentation": "https://www.home-assistant.io/components/currencylayer", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json new file mode 100644 index 00000000000000..28314bdf084bfe --- /dev/null +++ b/homeassistant/components/daikin/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "daikin", + "name": "Daikin", + "documentation": "https://www.home-assistant.io/components/daikin", + "requirements": [ + "pydaikin==1.3.1" + ], + "dependencies": [], + "codeowners": [ + "@fredrike", + "@rofrantz" + ] +} diff --git a/homeassistant/components/danfoss_air/manifest.json b/homeassistant/components/danfoss_air/manifest.json new file mode 100644 index 00000000000000..8af1707de650d6 --- /dev/null +++ b/homeassistant/components/danfoss_air/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "danfoss_air", + "name": "Danfoss air", + "documentation": "https://www.home-assistant.io/components/danfoss_air", + "requirements": [ + "pydanfossair==0.0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/darksky/manifest.json b/homeassistant/components/darksky/manifest.json new file mode 100644 index 00000000000000..e4e6482484cd21 --- /dev/null +++ b/homeassistant/components/darksky/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "darksky", + "name": "Darksky", + "documentation": "https://www.home-assistant.io/components/darksky", + "requirements": [ + "python-forecastio==1.4.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/datadog/manifest.json b/homeassistant/components/datadog/manifest.json new file mode 100644 index 00000000000000..40a2e82d53ac3e --- /dev/null +++ b/homeassistant/components/datadog/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "datadog", + "name": "Datadog", + "documentation": "https://www.home-assistant.io/components/datadog", + "requirements": [ + "datadog==0.15.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ddwrt/manifest.json b/homeassistant/components/ddwrt/manifest.json new file mode 100644 index 00000000000000..3c877a34841478 --- /dev/null +++ b/homeassistant/components/ddwrt/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ddwrt", + "name": "Ddwrt", + "documentation": "https://www.home-assistant.io/components/ddwrt", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json new file mode 100644 index 00000000000000..c68da4b566cb01 --- /dev/null +++ b/homeassistant/components/deconz/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "deconz", + "name": "Deconz", + "documentation": "https://www.home-assistant.io/components/deconz", + "requirements": [ + "pydeconz==54" + ], + "dependencies": [], + "codeowners": [ + "@kane610" + ] +} diff --git a/homeassistant/components/decora/manifest.json b/homeassistant/components/decora/manifest.json new file mode 100644 index 00000000000000..923a543e82788b --- /dev/null +++ b/homeassistant/components/decora/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "decora", + "name": "Decora", + "documentation": "https://www.home-assistant.io/components/decora", + "requirements": [ + "bluepy==1.1.4", + "decora==0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/decora_wifi/manifest.json b/homeassistant/components/decora_wifi/manifest.json new file mode 100644 index 00000000000000..3e938d743bd1b1 --- /dev/null +++ b/homeassistant/components/decora_wifi/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "decora_wifi", + "name": "Decora wifi", + "documentation": "https://www.home-assistant.io/components/decora_wifi", + "requirements": [ + "decora_wifi==1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/default_config/manifest.json b/homeassistant/components/default_config/manifest.json new file mode 100644 index 00000000000000..deda9c068050f0 --- /dev/null +++ b/homeassistant/components/default_config/manifest.json @@ -0,0 +1,25 @@ +{ + "domain": "default_config", + "name": "Default config", + "documentation": "https://www.home-assistant.io/components/default_config", + "requirements": [], + "dependencies": [ + "automation", + "cloud", + "config", + "conversation", + "frontend", + "history", + "logbook", + "map", + "mobile_app", + "person", + "script", + "stream", + "sun", + "system_health", + "updater", + "zeroconf" + ], + "codeowners": [] +} diff --git a/homeassistant/components/deluge/manifest.json b/homeassistant/components/deluge/manifest.json new file mode 100644 index 00000000000000..2b3c6d4c05505f --- /dev/null +++ b/homeassistant/components/deluge/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "deluge", + "name": "Deluge", + "documentation": "https://www.home-assistant.io/components/deluge", + "requirements": [ + "deluge-client==1.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/demo/manifest.json b/homeassistant/components/demo/manifest.json new file mode 100644 index 00000000000000..08cf75a3c5399d --- /dev/null +++ b/homeassistant/components/demo/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "demo", + "name": "Demo", + "documentation": "https://www.home-assistant.io/components/demo", + "requirements": [], + "dependencies": [ + "conversation", + "introduction", + "zone" + ], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/denon/manifest.json b/homeassistant/components/denon/manifest.json new file mode 100644 index 00000000000000..2068b72fa9d494 --- /dev/null +++ b/homeassistant/components/denon/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "denon", + "name": "Denon", + "documentation": "https://www.home-assistant.io/components/denon", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/denonavr/manifest.json b/homeassistant/components/denonavr/manifest.json new file mode 100644 index 00000000000000..df7d58169e056a --- /dev/null +++ b/homeassistant/components/denonavr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "denonavr", + "name": "Denonavr", + "documentation": "https://www.home-assistant.io/components/denonavr", + "requirements": [ + "denonavr==0.7.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/deutsche_bahn/manifest.json b/homeassistant/components/deutsche_bahn/manifest.json new file mode 100644 index 00000000000000..463c7d03fbb236 --- /dev/null +++ b/homeassistant/components/deutsche_bahn/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "deutsche_bahn", + "name": "Deutsche bahn", + "documentation": "https://www.home-assistant.io/components/deutsche_bahn", + "requirements": [ + "schiene==0.23" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/device_sun_light_trigger/manifest.json b/homeassistant/components/device_sun_light_trigger/manifest.json new file mode 100644 index 00000000000000..abe5a1d500cb81 --- /dev/null +++ b/homeassistant/components/device_sun_light_trigger/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "device_sun_light_trigger", + "name": "Device sun light trigger", + "documentation": "https://www.home-assistant.io/components/device_sun_light_trigger", + "requirements": [], + "dependencies": [ + "device_tracker", + "group", + "light" + ], + "codeowners": [] +} diff --git a/homeassistant/components/device_tracker/manifest.json b/homeassistant/components/device_tracker/manifest.json new file mode 100644 index 00000000000000..7b32f7845a6d5b --- /dev/null +++ b/homeassistant/components/device_tracker/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "device_tracker", + "name": "Device tracker", + "documentation": "https://www.home-assistant.io/components/device_tracker", + "requirements": [], + "dependencies": [ + "group", + "zone" + ], + "codeowners": [] +} diff --git a/homeassistant/components/dht/manifest.json b/homeassistant/components/dht/manifest.json new file mode 100644 index 00000000000000..05889bdd326106 --- /dev/null +++ b/homeassistant/components/dht/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dht", + "name": "Dht", + "documentation": "https://www.home-assistant.io/components/dht", + "requirements": [ + "Adafruit-DHT==1.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dialogflow/manifest.json b/homeassistant/components/dialogflow/manifest.json new file mode 100644 index 00000000000000..d136b8a984d536 --- /dev/null +++ b/homeassistant/components/dialogflow/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dialogflow", + "name": "Dialogflow", + "documentation": "https://www.home-assistant.io/components/dialogflow", + "requirements": [], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/digital_ocean/manifest.json b/homeassistant/components/digital_ocean/manifest.json new file mode 100644 index 00000000000000..2ef940f60bd963 --- /dev/null +++ b/homeassistant/components/digital_ocean/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "digital_ocean", + "name": "Digital ocean", + "documentation": "https://www.home-assistant.io/components/digital_ocean", + "requirements": [ + "python-digitalocean==1.13.2" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/digitalloggers/manifest.json b/homeassistant/components/digitalloggers/manifest.json new file mode 100644 index 00000000000000..990b39b21a5fdb --- /dev/null +++ b/homeassistant/components/digitalloggers/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "digitalloggers", + "name": "Digitalloggers", + "documentation": "https://www.home-assistant.io/components/digitalloggers", + "requirements": [ + "dlipower==0.7.165" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/directv/manifest.json b/homeassistant/components/directv/manifest.json new file mode 100644 index 00000000000000..7dbe6122ac1e34 --- /dev/null +++ b/homeassistant/components/directv/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "directv", + "name": "Directv", + "documentation": "https://www.home-assistant.io/components/directv", + "requirements": [ + "directpy==0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/discogs/manifest.json b/homeassistant/components/discogs/manifest.json new file mode 100644 index 00000000000000..ca304bce88bcff --- /dev/null +++ b/homeassistant/components/discogs/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "discogs", + "name": "Discogs", + "documentation": "https://www.home-assistant.io/components/discogs", + "requirements": [ + "discogs_client==2.2.1" + ], + "dependencies": [], + "codeowners": [ + "@thibmaek" + ] +} diff --git a/homeassistant/components/discord/manifest.json b/homeassistant/components/discord/manifest.json new file mode 100644 index 00000000000000..155e2b6806f0cf --- /dev/null +++ b/homeassistant/components/discord/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "discord", + "name": "Discord", + "documentation": "https://www.home-assistant.io/components/discord", + "requirements": [ + "discord.py==0.16.12" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/discovery/manifest.json b/homeassistant/components/discovery/manifest.json new file mode 100644 index 00000000000000..845e1af15d4053 --- /dev/null +++ b/homeassistant/components/discovery/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "discovery", + "name": "Discovery", + "documentation": "https://www.home-assistant.io/components/discovery", + "requirements": [ + "netdisco==2.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dlib_face_detect/manifest.json b/homeassistant/components/dlib_face_detect/manifest.json new file mode 100644 index 00000000000000..c2ede62ee5b015 --- /dev/null +++ b/homeassistant/components/dlib_face_detect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dlib_face_detect", + "name": "Dlib face detect", + "documentation": "https://www.home-assistant.io/components/dlib_face_detect", + "requirements": [ + "face_recognition==1.2.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dlib_face_identify/manifest.json b/homeassistant/components/dlib_face_identify/manifest.json new file mode 100644 index 00000000000000..388017f78bb45a --- /dev/null +++ b/homeassistant/components/dlib_face_identify/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dlib_face_identify", + "name": "Dlib face identify", + "documentation": "https://www.home-assistant.io/components/dlib_face_identify", + "requirements": [ + "face_recognition==1.2.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dlink/manifest.json b/homeassistant/components/dlink/manifest.json new file mode 100644 index 00000000000000..8f7d07eb0db39b --- /dev/null +++ b/homeassistant/components/dlink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dlink", + "name": "Dlink", + "documentation": "https://www.home-assistant.io/components/dlink", + "requirements": [ + "pyW215==0.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json new file mode 100644 index 00000000000000..be2e655454e815 --- /dev/null +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dlna_dmr", + "name": "Dlna dmr", + "documentation": "https://www.home-assistant.io/components/dlna_dmr", + "requirements": [ + "async-upnp-client==0.14.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dnsip/manifest.json b/homeassistant/components/dnsip/manifest.json new file mode 100644 index 00000000000000..2a92f1a0446bd4 --- /dev/null +++ b/homeassistant/components/dnsip/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dnsip", + "name": "Dnsip", + "documentation": "https://www.home-assistant.io/components/dnsip", + "requirements": [ + "aiodns==1.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dominos/manifest.json b/homeassistant/components/dominos/manifest.json new file mode 100644 index 00000000000000..f8d13b49f93327 --- /dev/null +++ b/homeassistant/components/dominos/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "dominos", + "name": "Dominos", + "documentation": "https://www.home-assistant.io/components/dominos", + "requirements": [ + "pizzapi==0.0.3" + ], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/doorbird/manifest.json b/homeassistant/components/doorbird/manifest.json new file mode 100644 index 00000000000000..f65af20f93c237 --- /dev/null +++ b/homeassistant/components/doorbird/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "doorbird", + "name": "Doorbird", + "documentation": "https://www.home-assistant.io/components/doorbird", + "requirements": [ + "doorbirdpy==2.0.6" + ], + "dependencies": [], + "codeowners": [ + "@oblogic7" + ] +} diff --git a/homeassistant/components/dovado/manifest.json b/homeassistant/components/dovado/manifest.json new file mode 100644 index 00000000000000..122d774c268221 --- /dev/null +++ b/homeassistant/components/dovado/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dovado", + "name": "Dovado", + "documentation": "https://www.home-assistant.io/components/dovado", + "requirements": [ + "dovado==0.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/downloader/manifest.json b/homeassistant/components/downloader/manifest.json new file mode 100644 index 00000000000000..514509c004d508 --- /dev/null +++ b/homeassistant/components/downloader/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "downloader", + "name": "Downloader", + "documentation": "https://www.home-assistant.io/components/downloader", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dsmr/manifest.json b/homeassistant/components/dsmr/manifest.json new file mode 100644 index 00000000000000..21c98d56d1d55a --- /dev/null +++ b/homeassistant/components/dsmr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dsmr", + "name": "Dsmr", + "documentation": "https://www.home-assistant.io/components/dsmr", + "requirements": [ + "dsmr_parser==0.12" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dte_energy_bridge/manifest.json b/homeassistant/components/dte_energy_bridge/manifest.json new file mode 100644 index 00000000000000..fbf7a00f8e6b0a --- /dev/null +++ b/homeassistant/components/dte_energy_bridge/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "dte_energy_bridge", + "name": "Dte energy bridge", + "documentation": "https://www.home-assistant.io/components/dte_energy_bridge", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dublin_bus_transport/manifest.json b/homeassistant/components/dublin_bus_transport/manifest.json new file mode 100644 index 00000000000000..fc13fddc9364e6 --- /dev/null +++ b/homeassistant/components/dublin_bus_transport/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "dublin_bus_transport", + "name": "Dublin bus transport", + "documentation": "https://www.home-assistant.io/components/dublin_bus_transport", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/duckdns/manifest.json b/homeassistant/components/duckdns/manifest.json new file mode 100644 index 00000000000000..ed38d35346f220 --- /dev/null +++ b/homeassistant/components/duckdns/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "duckdns", + "name": "Duckdns", + "documentation": "https://www.home-assistant.io/components/duckdns", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/duke_energy/manifest.json b/homeassistant/components/duke_energy/manifest.json new file mode 100644 index 00000000000000..602dfec801fd75 --- /dev/null +++ b/homeassistant/components/duke_energy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "duke_energy", + "name": "Duke energy", + "documentation": "https://www.home-assistant.io/components/duke_energy", + "requirements": [ + "pydukeenergy==0.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dunehd/manifest.json b/homeassistant/components/dunehd/manifest.json new file mode 100644 index 00000000000000..35e6c4a2449ed9 --- /dev/null +++ b/homeassistant/components/dunehd/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dunehd", + "name": "Dunehd", + "documentation": "https://www.home-assistant.io/components/dunehd", + "requirements": [ + "pdunehd==1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dwd_weather_warnings/manifest.json b/homeassistant/components/dwd_weather_warnings/manifest.json new file mode 100644 index 00000000000000..a2b21a9e0bf946 --- /dev/null +++ b/homeassistant/components/dwd_weather_warnings/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "dwd_weather_warnings", + "name": "Dwd weather warnings", + "documentation": "https://www.home-assistant.io/components/dwd_weather_warnings", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/dweet/manifest.json b/homeassistant/components/dweet/manifest.json new file mode 100644 index 00000000000000..e0a00620210afe --- /dev/null +++ b/homeassistant/components/dweet/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "dweet", + "name": "Dweet", + "documentation": "https://www.home-assistant.io/components/dweet", + "requirements": [ + "dweepy==0.3.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/dyson/manifest.json b/homeassistant/components/dyson/manifest.json new file mode 100644 index 00000000000000..7b956dd96c8324 --- /dev/null +++ b/homeassistant/components/dyson/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "dyson", + "name": "Dyson", + "documentation": "https://www.home-assistant.io/components/dyson", + "requirements": [ + "libpurecool==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ebox/manifest.json b/homeassistant/components/ebox/manifest.json new file mode 100644 index 00000000000000..16b033df8fdc09 --- /dev/null +++ b/homeassistant/components/ebox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ebox", + "name": "Ebox", + "documentation": "https://www.home-assistant.io/components/ebox", + "requirements": [ + "pyebox==1.1.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ebusd/manifest.json b/homeassistant/components/ebusd/manifest.json new file mode 100644 index 00000000000000..46b8fb761dcb7e --- /dev/null +++ b/homeassistant/components/ebusd/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ebusd", + "name": "Ebusd", + "documentation": "https://www.home-assistant.io/components/ebusd", + "requirements": [ + "ebusdpy==0.0.16" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ecoal_boiler/manifest.json b/homeassistant/components/ecoal_boiler/manifest.json new file mode 100644 index 00000000000000..5bd488e0ff4bd1 --- /dev/null +++ b/homeassistant/components/ecoal_boiler/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ecoal_boiler", + "name": "Ecoal boiler", + "documentation": "https://www.home-assistant.io/components/ecoal_boiler", + "requirements": [ + "ecoaliface==0.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ecobee/manifest.json b/homeassistant/components/ecobee/manifest.json new file mode 100644 index 00000000000000..3c7275a389512d --- /dev/null +++ b/homeassistant/components/ecobee/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ecobee", + "name": "Ecobee", + "documentation": "https://www.home-assistant.io/components/ecobee", + "requirements": [ + "python-ecobee-api==0.0.18" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/econet/manifest.json b/homeassistant/components/econet/manifest.json new file mode 100644 index 00000000000000..bd2cd19d519cf4 --- /dev/null +++ b/homeassistant/components/econet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "econet", + "name": "Econet", + "documentation": "https://www.home-assistant.io/components/econet", + "requirements": [ + "pyeconet==0.0.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ecovacs/manifest.json b/homeassistant/components/ecovacs/manifest.json new file mode 100644 index 00000000000000..d36768fb1b0328 --- /dev/null +++ b/homeassistant/components/ecovacs/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ecovacs", + "name": "Ecovacs", + "documentation": "https://www.home-assistant.io/components/ecovacs", + "requirements": [ + "sucks==0.9.3" + ], + "dependencies": [], + "codeowners": [ + "@OverloadUT" + ] +} diff --git a/homeassistant/components/eddystone_temperature/manifest.json b/homeassistant/components/eddystone_temperature/manifest.json new file mode 100644 index 00000000000000..4684655aa372a8 --- /dev/null +++ b/homeassistant/components/eddystone_temperature/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "eddystone_temperature", + "name": "Eddystone temperature", + "documentation": "https://www.home-assistant.io/components/eddystone_temperature", + "requirements": [ + "beacontools[scan]==1.2.3", + "construct==2.9.45" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/edimax/manifest.json b/homeassistant/components/edimax/manifest.json new file mode 100644 index 00000000000000..9fe0e4c50c9693 --- /dev/null +++ b/homeassistant/components/edimax/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "edimax", + "name": "Edimax", + "documentation": "https://www.home-assistant.io/components/edimax", + "requirements": [ + "pyedimax==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/edp_redy/manifest.json b/homeassistant/components/edp_redy/manifest.json new file mode 100644 index 00000000000000..90404b21678325 --- /dev/null +++ b/homeassistant/components/edp_redy/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "edp_redy", + "name": "Edp redy", + "documentation": "https://www.home-assistant.io/components/edp_redy", + "requirements": [ + "edp_redy==0.0.3" + ], + "dependencies": [], + "codeowners": [ + "@abmantis" + ] +} diff --git a/homeassistant/components/ee_brightbox/manifest.json b/homeassistant/components/ee_brightbox/manifest.json new file mode 100644 index 00000000000000..967f04228a825e --- /dev/null +++ b/homeassistant/components/ee_brightbox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ee_brightbox", + "name": "Ee brightbox", + "documentation": "https://www.home-assistant.io/components/ee_brightbox", + "requirements": [ + "eebrightbox==0.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/efergy/manifest.json b/homeassistant/components/efergy/manifest.json new file mode 100644 index 00000000000000..f4ca116a647084 --- /dev/null +++ b/homeassistant/components/efergy/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "efergy", + "name": "Efergy", + "documentation": "https://www.home-assistant.io/components/efergy", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/egardia/manifest.json b/homeassistant/components/egardia/manifest.json new file mode 100644 index 00000000000000..3a95b90db99001 --- /dev/null +++ b/homeassistant/components/egardia/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "egardia", + "name": "Egardia", + "documentation": "https://www.home-assistant.io/components/egardia", + "requirements": [ + "pythonegardia==1.0.39" + ], + "dependencies": [], + "codeowners": [ + "@jeroenterheerdt" + ] +} diff --git a/homeassistant/components/eight_sleep/manifest.json b/homeassistant/components/eight_sleep/manifest.json new file mode 100644 index 00000000000000..2b008c3c370fb7 --- /dev/null +++ b/homeassistant/components/eight_sleep/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "eight_sleep", + "name": "Eight sleep", + "documentation": "https://www.home-assistant.io/components/eight_sleep", + "requirements": [ + "pyeight==0.1.1" + ], + "dependencies": [], + "codeowners": [ + "@mezz64" + ] +} diff --git a/homeassistant/components/eliqonline/manifest.json b/homeassistant/components/eliqonline/manifest.json new file mode 100644 index 00000000000000..98d94fd009ea3d --- /dev/null +++ b/homeassistant/components/eliqonline/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "eliqonline", + "name": "Eliqonline", + "documentation": "https://www.home-assistant.io/components/eliqonline", + "requirements": [ + "eliqonline==1.2.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/elkm1/manifest.json b/homeassistant/components/elkm1/manifest.json new file mode 100644 index 00000000000000..73b48623260bfe --- /dev/null +++ b/homeassistant/components/elkm1/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "elkm1", + "name": "Elkm1", + "documentation": "https://www.home-assistant.io/components/elkm1", + "requirements": [ + "elkm1-lib==0.7.13" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/emby/manifest.json b/homeassistant/components/emby/manifest.json new file mode 100644 index 00000000000000..87688733e593ab --- /dev/null +++ b/homeassistant/components/emby/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "emby", + "name": "Emby", + "documentation": "https://www.home-assistant.io/components/emby", + "requirements": [ + "pyemby==1.6" + ], + "dependencies": [], + "codeowners": [ + "@mezz64" + ] +} diff --git a/homeassistant/components/emoncms/manifest.json b/homeassistant/components/emoncms/manifest.json new file mode 100644 index 00000000000000..90623c01d1be50 --- /dev/null +++ b/homeassistant/components/emoncms/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "emoncms", + "name": "Emoncms", + "documentation": "https://www.home-assistant.io/components/emoncms", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/emoncms_history/manifest.json b/homeassistant/components/emoncms_history/manifest.json new file mode 100644 index 00000000000000..0cb09e3fb73b88 --- /dev/null +++ b/homeassistant/components/emoncms_history/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "emoncms_history", + "name": "Emoncms history", + "documentation": "https://www.home-assistant.io/components/emoncms_history", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/emulated_hue/manifest.json b/homeassistant/components/emulated_hue/manifest.json new file mode 100644 index 00000000000000..75fcbc4c55500b --- /dev/null +++ b/homeassistant/components/emulated_hue/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "emulated_hue", + "name": "Emulated hue", + "documentation": "https://www.home-assistant.io/components/emulated_hue", + "requirements": [ + "aiohttp_cors==0.7.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/emulated_roku/manifest.json b/homeassistant/components/emulated_roku/manifest.json new file mode 100644 index 00000000000000..3b8eba396ec5cd --- /dev/null +++ b/homeassistant/components/emulated_roku/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "emulated_roku", + "name": "Emulated roku", + "documentation": "https://www.home-assistant.io/components/emulated_roku", + "requirements": [ + "emulated_roku==0.1.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/enigma2/manifest.json b/homeassistant/components/enigma2/manifest.json new file mode 100644 index 00000000000000..78eb9f9deff82a --- /dev/null +++ b/homeassistant/components/enigma2/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "enigma2", + "name": "Enigma2", + "documentation": "https://www.home-assistant.io/components/enigma2", + "requirements": [ + "openwebifpy==3.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/enocean/manifest.json b/homeassistant/components/enocean/manifest.json new file mode 100644 index 00000000000000..7c4d7c0b8d9b28 --- /dev/null +++ b/homeassistant/components/enocean/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "enocean", + "name": "Enocean", + "documentation": "https://www.home-assistant.io/components/enocean", + "requirements": [ + "enocean==0.40" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/enphase_envoy/manifest.json b/homeassistant/components/enphase_envoy/manifest.json new file mode 100644 index 00000000000000..6fee88b39fca80 --- /dev/null +++ b/homeassistant/components/enphase_envoy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "enphase_envoy", + "name": "Enphase envoy", + "documentation": "https://www.home-assistant.io/components/enphase_envoy", + "requirements": [ + "envoy_reader==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/entur_public_transport/manifest.json b/homeassistant/components/entur_public_transport/manifest.json new file mode 100644 index 00000000000000..b2b60cff95a486 --- /dev/null +++ b/homeassistant/components/entur_public_transport/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "entur_public_transport", + "name": "Entur public transport", + "documentation": "https://www.home-assistant.io/components/entur_public_transport", + "requirements": [ + "enturclient==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/envirophat/manifest.json b/homeassistant/components/envirophat/manifest.json new file mode 100644 index 00000000000000..c69a66d43f85eb --- /dev/null +++ b/homeassistant/components/envirophat/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "envirophat", + "name": "Envirophat", + "documentation": "https://www.home-assistant.io/components/envirophat", + "requirements": [ + "envirophat==0.0.6", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/envisalink/manifest.json b/homeassistant/components/envisalink/manifest.json new file mode 100644 index 00000000000000..b34aa08951ca85 --- /dev/null +++ b/homeassistant/components/envisalink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "envisalink", + "name": "Envisalink", + "documentation": "https://www.home-assistant.io/components/envisalink", + "requirements": [ + "pyenvisalink==3.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ephember/manifest.json b/homeassistant/components/ephember/manifest.json new file mode 100644 index 00000000000000..3fed307aed5f3e --- /dev/null +++ b/homeassistant/components/ephember/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ephember", + "name": "Ephember", + "documentation": "https://www.home-assistant.io/components/ephember", + "requirements": [ + "pyephember==0.2.0" + ], + "dependencies": [], + "codeowners": [ + "@ttroy50" + ] +} diff --git a/homeassistant/components/epson/manifest.json b/homeassistant/components/epson/manifest.json new file mode 100644 index 00000000000000..e6623b83013ad6 --- /dev/null +++ b/homeassistant/components/epson/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "epson", + "name": "Epson", + "documentation": "https://www.home-assistant.io/components/epson", + "requirements": [ + "epson-projector==0.1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/eq3btsmart/manifest.json b/homeassistant/components/eq3btsmart/manifest.json new file mode 100644 index 00000000000000..6d13c79bcec097 --- /dev/null +++ b/homeassistant/components/eq3btsmart/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "eq3btsmart", + "name": "Eq3btsmart", + "documentation": "https://www.home-assistant.io/components/eq3btsmart", + "requirements": [ + "construct==2.9.45", + "python-eq3bt==0.1.9" + ], + "dependencies": [], + "codeowners": [ + "@rytilahti" + ] +} diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json new file mode 100644 index 00000000000000..b00cdf9607d90d --- /dev/null +++ b/homeassistant/components/esphome/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "esphome", + "name": "Esphome", + "documentation": "https://www.home-assistant.io/components/esphome", + "requirements": [ + "aioesphomeapi==1.7.0" + ], + "dependencies": [], + "codeowners": [ + "@OttoWinter" + ] +} diff --git a/homeassistant/components/etherscan/manifest.json b/homeassistant/components/etherscan/manifest.json new file mode 100644 index 00000000000000..452d1c4c475343 --- /dev/null +++ b/homeassistant/components/etherscan/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "etherscan", + "name": "Etherscan", + "documentation": "https://www.home-assistant.io/components/etherscan", + "requirements": [ + "python-etherscan-api==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/eufy/manifest.json b/homeassistant/components/eufy/manifest.json new file mode 100644 index 00000000000000..ec7f1fe7072231 --- /dev/null +++ b/homeassistant/components/eufy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "eufy", + "name": "Eufy", + "documentation": "https://www.home-assistant.io/components/eufy", + "requirements": [ + "lakeside==0.12" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/everlights/manifest.json b/homeassistant/components/everlights/manifest.json new file mode 100644 index 00000000000000..9c2e1b2ae4f667 --- /dev/null +++ b/homeassistant/components/everlights/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "everlights", + "name": "Everlights", + "documentation": "https://www.home-assistant.io/components/everlights", + "requirements": [ + "pyeverlights==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/evohome/manifest.json b/homeassistant/components/evohome/manifest.json new file mode 100644 index 00000000000000..b612baa476a0b5 --- /dev/null +++ b/homeassistant/components/evohome/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "evohome", + "name": "Evohome", + "documentation": "https://www.home-assistant.io/components/evohome", + "requirements": [ + "evohomeclient==0.3.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/facebook/manifest.json b/homeassistant/components/facebook/manifest.json new file mode 100644 index 00000000000000..9632906a25a74e --- /dev/null +++ b/homeassistant/components/facebook/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "facebook", + "name": "Facebook", + "documentation": "https://www.home-assistant.io/components/facebook", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/facebox/manifest.json b/homeassistant/components/facebox/manifest.json new file mode 100644 index 00000000000000..4a3eefc135c563 --- /dev/null +++ b/homeassistant/components/facebox/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "facebox", + "name": "Facebox", + "documentation": "https://www.home-assistant.io/components/facebox", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fail2ban/manifest.json b/homeassistant/components/fail2ban/manifest.json new file mode 100644 index 00000000000000..fc60658a3f2c6a --- /dev/null +++ b/homeassistant/components/fail2ban/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "fail2ban", + "name": "Fail2ban", + "documentation": "https://www.home-assistant.io/components/fail2ban", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/familyhub/manifest.json b/homeassistant/components/familyhub/manifest.json new file mode 100644 index 00000000000000..48a73dfb0300ba --- /dev/null +++ b/homeassistant/components/familyhub/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "familyhub", + "name": "Familyhub", + "documentation": "https://www.home-assistant.io/components/familyhub", + "requirements": [ + "python-family-hub-local==0.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fan/manifest.json b/homeassistant/components/fan/manifest.json new file mode 100644 index 00000000000000..85bb982d2d1f46 --- /dev/null +++ b/homeassistant/components/fan/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fan", + "name": "Fan", + "documentation": "https://www.home-assistant.io/components/fan", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/fastdotcom/manifest.json b/homeassistant/components/fastdotcom/manifest.json new file mode 100644 index 00000000000000..f4bf021380c986 --- /dev/null +++ b/homeassistant/components/fastdotcom/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fastdotcom", + "name": "Fastdotcom", + "documentation": "https://www.home-assistant.io/components/fastdotcom", + "requirements": [ + "fastdotcom==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fedex/manifest.json b/homeassistant/components/fedex/manifest.json new file mode 100644 index 00000000000000..b34a8b8383ef85 --- /dev/null +++ b/homeassistant/components/fedex/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fedex", + "name": "Fedex", + "documentation": "https://www.home-assistant.io/components/fedex", + "requirements": [ + "fedexdeliverymanager==1.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/feedreader/manifest.json b/homeassistant/components/feedreader/manifest.json new file mode 100644 index 00000000000000..e458d30073e8a7 --- /dev/null +++ b/homeassistant/components/feedreader/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "feedreader", + "name": "Feedreader", + "documentation": "https://www.home-assistant.io/components/feedreader", + "requirements": [ + "feedparser-homeassistant==5.2.2.dev1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ffmpeg/manifest.json b/homeassistant/components/ffmpeg/manifest.json new file mode 100644 index 00000000000000..4a3695e7dcc52f --- /dev/null +++ b/homeassistant/components/ffmpeg/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ffmpeg", + "name": "Ffmpeg", + "documentation": "https://www.home-assistant.io/components/ffmpeg", + "requirements": [ + "ha-ffmpeg==2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ffmpeg_motion/manifest.json b/homeassistant/components/ffmpeg_motion/manifest.json new file mode 100644 index 00000000000000..bc5dfaa4209b34 --- /dev/null +++ b/homeassistant/components/ffmpeg_motion/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ffmpeg_motion", + "name": "Ffmpeg motion", + "documentation": "https://www.home-assistant.io/components/ffmpeg_motion", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ffmpeg_noise/manifest.json b/homeassistant/components/ffmpeg_noise/manifest.json new file mode 100644 index 00000000000000..6fdf07899fd7ba --- /dev/null +++ b/homeassistant/components/ffmpeg_noise/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ffmpeg_noise", + "name": "Ffmpeg noise", + "documentation": "https://www.home-assistant.io/components/ffmpeg_noise", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fibaro/manifest.json b/homeassistant/components/fibaro/manifest.json new file mode 100644 index 00000000000000..3574e6254ded39 --- /dev/null +++ b/homeassistant/components/fibaro/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fibaro", + "name": "Fibaro", + "documentation": "https://www.home-assistant.io/components/fibaro", + "requirements": [ + "fiblary3==0.1.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fido/manifest.json b/homeassistant/components/fido/manifest.json new file mode 100644 index 00000000000000..343a21ff072fae --- /dev/null +++ b/homeassistant/components/fido/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fido", + "name": "Fido", + "documentation": "https://www.home-assistant.io/components/fido", + "requirements": [ + "pyfido==2.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/file/manifest.json b/homeassistant/components/file/manifest.json new file mode 100644 index 00000000000000..581b0e14156666 --- /dev/null +++ b/homeassistant/components/file/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "file", + "name": "File", + "documentation": "https://www.home-assistant.io/components/file", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/filesize/manifest.json b/homeassistant/components/filesize/manifest.json new file mode 100644 index 00000000000000..f76bcd27466c64 --- /dev/null +++ b/homeassistant/components/filesize/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "filesize", + "name": "Filesize", + "documentation": "https://www.home-assistant.io/components/filesize", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/filter/manifest.json b/homeassistant/components/filter/manifest.json new file mode 100644 index 00000000000000..28f061d26f7c5c --- /dev/null +++ b/homeassistant/components/filter/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "filter", + "name": "Filter", + "documentation": "https://www.home-assistant.io/components/filter", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/fints/manifest.json b/homeassistant/components/fints/manifest.json new file mode 100644 index 00000000000000..e3580676290b9a --- /dev/null +++ b/homeassistant/components/fints/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fints", + "name": "Fints", + "documentation": "https://www.home-assistant.io/components/fints", + "requirements": [ + "fints==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fitbit/manifest.json b/homeassistant/components/fitbit/manifest.json new file mode 100644 index 00000000000000..d1335a1347d4e1 --- /dev/null +++ b/homeassistant/components/fitbit/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "fitbit", + "name": "Fitbit", + "documentation": "https://www.home-assistant.io/components/fitbit", + "requirements": [ + "fitbit==0.3.0" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/fixer/manifest.json b/homeassistant/components/fixer/manifest.json new file mode 100644 index 00000000000000..1e010bb06ed0c7 --- /dev/null +++ b/homeassistant/components/fixer/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "fixer", + "name": "Fixer", + "documentation": "https://www.home-assistant.io/components/fixer", + "requirements": [ + "fixerio==1.0.0a0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/flexit/manifest.json b/homeassistant/components/flexit/manifest.json new file mode 100644 index 00000000000000..1af86243f8697f --- /dev/null +++ b/homeassistant/components/flexit/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "flexit", + "name": "Flexit", + "documentation": "https://www.home-assistant.io/components/flexit", + "requirements": [ + "pyflexit==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/flic/manifest.json b/homeassistant/components/flic/manifest.json new file mode 100644 index 00000000000000..827bcb167c397d --- /dev/null +++ b/homeassistant/components/flic/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "flic", + "name": "Flic", + "documentation": "https://www.home-assistant.io/components/flic", + "requirements": [ + "pyflic-homeassistant==0.4.dev0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/flock/manifest.json b/homeassistant/components/flock/manifest.json new file mode 100644 index 00000000000000..a5af541eeeef38 --- /dev/null +++ b/homeassistant/components/flock/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "flock", + "name": "Flock", + "documentation": "https://www.home-assistant.io/components/flock", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/flunearyou/manifest.json b/homeassistant/components/flunearyou/manifest.json new file mode 100644 index 00000000000000..76053f75081735 --- /dev/null +++ b/homeassistant/components/flunearyou/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "flunearyou", + "name": "Flunearyou", + "documentation": "https://www.home-assistant.io/components/flunearyou", + "requirements": [ + "pyflunearyou==1.0.3" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/flux/manifest.json b/homeassistant/components/flux/manifest.json new file mode 100644 index 00000000000000..8c07a70bca6ff8 --- /dev/null +++ b/homeassistant/components/flux/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "flux", + "name": "Flux", + "documentation": "https://www.home-assistant.io/components/flux", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json new file mode 100644 index 00000000000000..0d00275200caba --- /dev/null +++ b/homeassistant/components/flux_led/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "flux_led", + "name": "Flux led", + "documentation": "https://www.home-assistant.io/components/flux_led", + "requirements": [ + "flux_led==0.22" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/folder/manifest.json b/homeassistant/components/folder/manifest.json new file mode 100644 index 00000000000000..7a0bf76e0aa310 --- /dev/null +++ b/homeassistant/components/folder/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "folder", + "name": "Folder", + "documentation": "https://www.home-assistant.io/components/folder", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/folder_watcher/manifest.json b/homeassistant/components/folder_watcher/manifest.json new file mode 100644 index 00000000000000..1a5b547e5ff21c --- /dev/null +++ b/homeassistant/components/folder_watcher/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "folder_watcher", + "name": "Folder watcher", + "documentation": "https://www.home-assistant.io/components/folder_watcher", + "requirements": [ + "watchdog==0.8.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/foobot/manifest.json b/homeassistant/components/foobot/manifest.json new file mode 100644 index 00000000000000..9ed95597e41700 --- /dev/null +++ b/homeassistant/components/foobot/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "foobot", + "name": "Foobot", + "documentation": "https://www.home-assistant.io/components/foobot", + "requirements": [ + "foobot_async==0.3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/foscam/manifest.json b/homeassistant/components/foscam/manifest.json new file mode 100644 index 00000000000000..b05aa956b42a84 --- /dev/null +++ b/homeassistant/components/foscam/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "foscam", + "name": "Foscam", + "documentation": "https://www.home-assistant.io/components/foscam", + "requirements": [ + "libpyfoscam==1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/foursquare/manifest.json b/homeassistant/components/foursquare/manifest.json new file mode 100644 index 00000000000000..84a98ca033625b --- /dev/null +++ b/homeassistant/components/foursquare/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "foursquare", + "name": "Foursquare", + "documentation": "https://www.home-assistant.io/components/foursquare", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/free_mobile/manifest.json b/homeassistant/components/free_mobile/manifest.json new file mode 100644 index 00000000000000..b8a40c3fc1d2ae --- /dev/null +++ b/homeassistant/components/free_mobile/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "free_mobile", + "name": "Free mobile", + "documentation": "https://www.home-assistant.io/components/free_mobile", + "requirements": [ + "freesms==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/freebox/manifest.json b/homeassistant/components/freebox/manifest.json new file mode 100644 index 00000000000000..9ee134d41709f3 --- /dev/null +++ b/homeassistant/components/freebox/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "freebox", + "name": "Freebox", + "documentation": "https://www.home-assistant.io/components/freebox", + "requirements": [ + "aiofreepybox==0.0.8" + ], + "dependencies": [], + "codeowners": [ + "@snoof85" + ] +} diff --git a/homeassistant/components/freedns/manifest.json b/homeassistant/components/freedns/manifest.json new file mode 100644 index 00000000000000..63f929754db60f --- /dev/null +++ b/homeassistant/components/freedns/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "freedns", + "name": "Freedns", + "documentation": "https://www.home-assistant.io/components/freedns", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fritz/manifest.json b/homeassistant/components/fritz/manifest.json new file mode 100644 index 00000000000000..b2aacbd48ad795 --- /dev/null +++ b/homeassistant/components/fritz/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fritz", + "name": "Fritz", + "documentation": "https://www.home-assistant.io/components/fritz", + "requirements": [ + "fritzconnection==0.6.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fritzbox/manifest.json b/homeassistant/components/fritzbox/manifest.json new file mode 100644 index 00000000000000..1ed18140bd2842 --- /dev/null +++ b/homeassistant/components/fritzbox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fritzbox", + "name": "Fritzbox", + "documentation": "https://www.home-assistant.io/components/fritzbox", + "requirements": [ + "pyfritzhome==0.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fritzbox_callmonitor/manifest.json b/homeassistant/components/fritzbox_callmonitor/manifest.json new file mode 100644 index 00000000000000..19f232ed6677c5 --- /dev/null +++ b/homeassistant/components/fritzbox_callmonitor/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fritzbox_callmonitor", + "name": "Fritzbox callmonitor", + "documentation": "https://www.home-assistant.io/components/fritzbox_callmonitor", + "requirements": [ + "fritzconnection==0.6.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fritzbox_netmonitor/manifest.json b/homeassistant/components/fritzbox_netmonitor/manifest.json new file mode 100644 index 00000000000000..ac1ce2893e488d --- /dev/null +++ b/homeassistant/components/fritzbox_netmonitor/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fritzbox_netmonitor", + "name": "Fritzbox netmonitor", + "documentation": "https://www.home-assistant.io/components/fritzbox_netmonitor", + "requirements": [ + "fritzconnection==0.6.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/fritzdect/manifest.json b/homeassistant/components/fritzdect/manifest.json new file mode 100644 index 00000000000000..98d628fe078aaa --- /dev/null +++ b/homeassistant/components/fritzdect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "fritzdect", + "name": "Fritzdect", + "documentation": "https://www.home-assistant.io/components/fritzdect", + "requirements": [ + "fritzhome==1.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json new file mode 100644 index 00000000000000..5be4593a8bd2eb --- /dev/null +++ b/homeassistant/components/frontend/manifest.json @@ -0,0 +1,20 @@ +{ + "domain": "frontend", + "name": "Home Assistant Frontend", + "documentation": "https://www.home-assistant.io/components/frontend", + "requirements": [ + "home-assistant-frontend==20190331.0" + ], + "dependencies": [ + "api", + "auth", + "http", + "lovelace", + "onboarding", + "system_log", + "websocket_api" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/frontier_silicon/manifest.json b/homeassistant/components/frontier_silicon/manifest.json new file mode 100644 index 00000000000000..0e20a509d1f226 --- /dev/null +++ b/homeassistant/components/frontier_silicon/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "frontier_silicon", + "name": "Frontier silicon", + "documentation": "https://www.home-assistant.io/components/frontier_silicon", + "requirements": [ + "afsapi==0.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/futurenow/manifest.json b/homeassistant/components/futurenow/manifest.json new file mode 100644 index 00000000000000..5191ab611acf2e --- /dev/null +++ b/homeassistant/components/futurenow/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "futurenow", + "name": "Futurenow", + "documentation": "https://www.home-assistant.io/components/futurenow", + "requirements": [ + "pyfnip==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/garadget/manifest.json b/homeassistant/components/garadget/manifest.json new file mode 100644 index 00000000000000..d3781f81d046aa --- /dev/null +++ b/homeassistant/components/garadget/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "garadget", + "name": "Garadget", + "documentation": "https://www.home-assistant.io/components/garadget", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gc100/manifest.json b/homeassistant/components/gc100/manifest.json new file mode 100644 index 00000000000000..96d792196ce956 --- /dev/null +++ b/homeassistant/components/gc100/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gc100", + "name": "Gc100", + "documentation": "https://www.home-assistant.io/components/gc100", + "requirements": [ + "python-gc100==1.0.3a" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gearbest/manifest.json b/homeassistant/components/gearbest/manifest.json new file mode 100644 index 00000000000000..39ceca41d08028 --- /dev/null +++ b/homeassistant/components/gearbest/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "gearbest", + "name": "Gearbest", + "documentation": "https://www.home-assistant.io/components/gearbest", + "requirements": [ + "gearbest_parser==1.0.7" + ], + "dependencies": [], + "codeowners": [ + "@HerrHofrat" + ] +} diff --git a/homeassistant/components/geizhals/manifest.json b/homeassistant/components/geizhals/manifest.json new file mode 100644 index 00000000000000..d53bceaa1455c8 --- /dev/null +++ b/homeassistant/components/geizhals/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "geizhals", + "name": "Geizhals", + "documentation": "https://www.home-assistant.io/components/geizhals", + "requirements": [ + "geizhals==0.0.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/generic/manifest.json b/homeassistant/components/generic/manifest.json new file mode 100644 index 00000000000000..e4d3622a562539 --- /dev/null +++ b/homeassistant/components/generic/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "generic", + "name": "Generic", + "documentation": "https://www.home-assistant.io/components/generic", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/generic_thermostat/manifest.json b/homeassistant/components/generic_thermostat/manifest.json new file mode 100644 index 00000000000000..67306b1e7cdae8 --- /dev/null +++ b/homeassistant/components/generic_thermostat/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "generic_thermostat", + "name": "Generic thermostat", + "documentation": "https://www.home-assistant.io/components/generic_thermostat", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/geo_json_events/manifest.json b/homeassistant/components/geo_json_events/manifest.json new file mode 100644 index 00000000000000..8e4d7b8a7cdb51 --- /dev/null +++ b/homeassistant/components/geo_json_events/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "geo_json_events", + "name": "Geo json events", + "documentation": "https://www.home-assistant.io/components/geo_json_events", + "requirements": [ + "geojson_client==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/geo_location/manifest.json b/homeassistant/components/geo_location/manifest.json new file mode 100644 index 00000000000000..83b4241284e89a --- /dev/null +++ b/homeassistant/components/geo_location/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "geo_location", + "name": "Geo location", + "documentation": "https://www.home-assistant.io/components/geo_location", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/geo_rss_events/manifest.json b/homeassistant/components/geo_rss_events/manifest.json new file mode 100644 index 00000000000000..bce6758b0fe9ea --- /dev/null +++ b/homeassistant/components/geo_rss_events/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "geo_rss_events", + "name": "Geo rss events", + "documentation": "https://www.home-assistant.io/components/geo_rss_events", + "requirements": [ + "georss_generic_client==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/geofency/manifest.json b/homeassistant/components/geofency/manifest.json new file mode 100644 index 00000000000000..576d0e419a733e --- /dev/null +++ b/homeassistant/components/geofency/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "geofency", + "name": "Geofency", + "documentation": "https://www.home-assistant.io/components/geofency", + "requirements": [], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/github/manifest.json b/homeassistant/components/github/manifest.json new file mode 100644 index 00000000000000..a2c2ae04376bd9 --- /dev/null +++ b/homeassistant/components/github/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "github", + "name": "Github", + "documentation": "https://www.home-assistant.io/components/github", + "requirements": [ + "PyGithub==1.43.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gitlab_ci/manifest.json b/homeassistant/components/gitlab_ci/manifest.json new file mode 100644 index 00000000000000..4ea04de9e02394 --- /dev/null +++ b/homeassistant/components/gitlab_ci/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gitlab_ci", + "name": "Gitlab ci", + "documentation": "https://www.home-assistant.io/components/gitlab_ci", + "requirements": [ + "python-gitlab==1.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gitter/manifest.json b/homeassistant/components/gitter/manifest.json new file mode 100644 index 00000000000000..6600e46a4ce953 --- /dev/null +++ b/homeassistant/components/gitter/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "gitter", + "name": "Gitter", + "documentation": "https://www.home-assistant.io/components/gitter", + "requirements": [ + "gitterpy==0.1.7" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/glances/manifest.json b/homeassistant/components/glances/manifest.json new file mode 100644 index 00000000000000..621bca8c4309ac --- /dev/null +++ b/homeassistant/components/glances/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "glances", + "name": "Glances", + "documentation": "https://www.home-assistant.io/components/glances", + "requirements": [ + "glances_api==0.2.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/gntp/manifest.json b/homeassistant/components/gntp/manifest.json new file mode 100644 index 00000000000000..7315e3c7c849be --- /dev/null +++ b/homeassistant/components/gntp/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "gntp", + "name": "Gntp", + "documentation": "https://www.home-assistant.io/components/gntp", + "requirements": [ + "gntp==1.0.3" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/goalfeed/manifest.json b/homeassistant/components/goalfeed/manifest.json new file mode 100644 index 00000000000000..861abe0b462d9c --- /dev/null +++ b/homeassistant/components/goalfeed/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "goalfeed", + "name": "Goalfeed", + "documentation": "https://www.home-assistant.io/components/goalfeed", + "requirements": [ + "pysher==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gogogate2/manifest.json b/homeassistant/components/gogogate2/manifest.json new file mode 100644 index 00000000000000..3f3f2c25d0c784 --- /dev/null +++ b/homeassistant/components/gogogate2/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gogogate2", + "name": "Gogogate2", + "documentation": "https://www.home-assistant.io/components/gogogate2", + "requirements": [ + "pygogogate2==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json new file mode 100644 index 00000000000000..2db50b2d5b9dbe --- /dev/null +++ b/homeassistant/components/google/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "google", + "name": "Google", + "documentation": "https://www.home-assistant.io/components/google", + "requirements": [ + "gTTS-token==1.1.3", + "google-api-python-client==1.6.4", + "httplib2==0.10.3", + "oauth2client==4.0.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/google_assistant/manifest.json b/homeassistant/components/google_assistant/manifest.json new file mode 100644 index 00000000000000..ff916930216548 --- /dev/null +++ b/homeassistant/components/google_assistant/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "google_assistant", + "name": "Google assistant", + "documentation": "https://www.home-assistant.io/components/google_assistant", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/google_domains/manifest.json b/homeassistant/components/google_domains/manifest.json new file mode 100644 index 00000000000000..190e5860ee60f6 --- /dev/null +++ b/homeassistant/components/google_domains/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "google_domains", + "name": "Google domains", + "documentation": "https://www.home-assistant.io/components/google_domains", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/google_maps/manifest.json b/homeassistant/components/google_maps/manifest.json new file mode 100644 index 00000000000000..7d6aeeef041bb3 --- /dev/null +++ b/homeassistant/components/google_maps/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "google_maps", + "name": "Google maps", + "documentation": "https://www.home-assistant.io/components/google_maps", + "requirements": [ + "locationsharinglib==3.0.11" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/google_pubsub/manifest.json b/homeassistant/components/google_pubsub/manifest.json new file mode 100644 index 00000000000000..ff61ad0e05df57 --- /dev/null +++ b/homeassistant/components/google_pubsub/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "google_pubsub", + "name": "Google pubsub", + "documentation": "https://www.home-assistant.io/components/google_pubsub", + "requirements": [ + "google-cloud-pubsub==0.39.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/google_travel_time/manifest.json b/homeassistant/components/google_travel_time/manifest.json new file mode 100644 index 00000000000000..eaa168332a63d2 --- /dev/null +++ b/homeassistant/components/google_travel_time/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "google_travel_time", + "name": "Google travel time", + "documentation": "https://www.home-assistant.io/components/google_travel_time", + "requirements": [ + "googlemaps==2.5.1" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/google_wifi/manifest.json b/homeassistant/components/google_wifi/manifest.json new file mode 100644 index 00000000000000..6e840458207e12 --- /dev/null +++ b/homeassistant/components/google_wifi/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "google_wifi", + "name": "Google wifi", + "documentation": "https://www.home-assistant.io/components/google_wifi", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/googlehome/manifest.json b/homeassistant/components/googlehome/manifest.json new file mode 100644 index 00000000000000..107e7d634f0f01 --- /dev/null +++ b/homeassistant/components/googlehome/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "googlehome", + "name": "Googlehome", + "documentation": "https://www.home-assistant.io/components/googlehome", + "requirements": [ + "googledevices==1.0.2" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/gpmdp/manifest.json b/homeassistant/components/gpmdp/manifest.json new file mode 100644 index 00000000000000..97e97e7645cf3b --- /dev/null +++ b/homeassistant/components/gpmdp/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gpmdp", + "name": "Gpmdp", + "documentation": "https://www.home-assistant.io/components/gpmdp", + "requirements": [ + "websocket-client==0.54.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gpsd/manifest.json b/homeassistant/components/gpsd/manifest.json new file mode 100644 index 00000000000000..b35d5cb1850c62 --- /dev/null +++ b/homeassistant/components/gpsd/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "gpsd", + "name": "Gpsd", + "documentation": "https://www.home-assistant.io/components/gpsd", + "requirements": [ + "gps3==0.33.3" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/gpslogger/manifest.json b/homeassistant/components/gpslogger/manifest.json new file mode 100644 index 00000000000000..2d2166c1bb1711 --- /dev/null +++ b/homeassistant/components/gpslogger/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gpslogger", + "name": "Gpslogger", + "documentation": "https://www.home-assistant.io/components/gpslogger", + "requirements": [], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/graphite/manifest.json b/homeassistant/components/graphite/manifest.json new file mode 100644 index 00000000000000..a5eefc5af04375 --- /dev/null +++ b/homeassistant/components/graphite/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "graphite", + "name": "Graphite", + "documentation": "https://www.home-assistant.io/components/graphite", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/greeneye_monitor/manifest.json b/homeassistant/components/greeneye_monitor/manifest.json new file mode 100644 index 00000000000000..7bfb87ede474ed --- /dev/null +++ b/homeassistant/components/greeneye_monitor/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "greeneye_monitor", + "name": "Greeneye monitor", + "documentation": "https://www.home-assistant.io/components/greeneye_monitor", + "requirements": [ + "greeneye_monitor==1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/greenwave/manifest.json b/homeassistant/components/greenwave/manifest.json new file mode 100644 index 00000000000000..1032b5eaf2a2ed --- /dev/null +++ b/homeassistant/components/greenwave/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "greenwave", + "name": "Greenwave", + "documentation": "https://www.home-assistant.io/components/greenwave", + "requirements": [ + "greenwavereality==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/group/manifest.json b/homeassistant/components/group/manifest.json new file mode 100644 index 00000000000000..aa99e20a4dfe46 --- /dev/null +++ b/homeassistant/components/group/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "group", + "name": "Group", + "documentation": "https://www.home-assistant.io/components/group", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/gstreamer/manifest.json b/homeassistant/components/gstreamer/manifest.json new file mode 100644 index 00000000000000..6bfb8abbe0b5b8 --- /dev/null +++ b/homeassistant/components/gstreamer/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gstreamer", + "name": "Gstreamer", + "documentation": "https://www.home-assistant.io/components/gstreamer", + "requirements": [ + "gstreamer-player==1.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/gtfs/manifest.json b/homeassistant/components/gtfs/manifest.json new file mode 100644 index 00000000000000..1c7ddbd65ee90d --- /dev/null +++ b/homeassistant/components/gtfs/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "gtfs", + "name": "Gtfs", + "documentation": "https://www.home-assistant.io/components/gtfs", + "requirements": [ + "pygtfs==0.1.5" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/gtt/manifest.json b/homeassistant/components/gtt/manifest.json new file mode 100644 index 00000000000000..142261fe155717 --- /dev/null +++ b/homeassistant/components/gtt/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "gtt", + "name": "Gtt", + "documentation": "https://www.home-assistant.io/components/gtt", + "requirements": [ + "pygtt==1.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/habitica/manifest.json b/homeassistant/components/habitica/manifest.json new file mode 100644 index 00000000000000..b8e622823d31d3 --- /dev/null +++ b/homeassistant/components/habitica/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "habitica", + "name": "Habitica", + "documentation": "https://www.home-assistant.io/components/habitica", + "requirements": [ + "habitipy==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hangouts/manifest.json b/homeassistant/components/hangouts/manifest.json new file mode 100644 index 00000000000000..a17bd76adb469d --- /dev/null +++ b/homeassistant/components/hangouts/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hangouts", + "name": "Hangouts", + "documentation": "https://www.home-assistant.io/components/hangouts", + "requirements": [ + "hangups==0.4.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/harman_kardon_avr/manifest.json b/homeassistant/components/harman_kardon_avr/manifest.json new file mode 100644 index 00000000000000..eecbf0edd63e77 --- /dev/null +++ b/homeassistant/components/harman_kardon_avr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "harman_kardon_avr", + "name": "Harman kardon avr", + "documentation": "https://www.home-assistant.io/components/harman_kardon_avr", + "requirements": [ + "hkavr==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/harmony/manifest.json b/homeassistant/components/harmony/manifest.json new file mode 100644 index 00000000000000..c82e9b7bf104d7 --- /dev/null +++ b/homeassistant/components/harmony/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "harmony", + "name": "Harmony", + "documentation": "https://www.home-assistant.io/components/harmony", + "requirements": [ + "aioharmony==0.1.8" + ], + "dependencies": [], + "codeowners": [ + "@ehendrix23" + ] +} diff --git a/homeassistant/components/hassio/manifest.json b/homeassistant/components/hassio/manifest.json new file mode 100644 index 00000000000000..e412f587abd616 --- /dev/null +++ b/homeassistant/components/hassio/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "hassio", + "name": "Hass.io", + "documentation": "https://www.home-assistant.io/hassio", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/hassio" + ] +} diff --git a/homeassistant/components/haveibeenpwned/manifest.json b/homeassistant/components/haveibeenpwned/manifest.json new file mode 100644 index 00000000000000..f0b0561e170eac --- /dev/null +++ b/homeassistant/components/haveibeenpwned/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "haveibeenpwned", + "name": "Haveibeenpwned", + "documentation": "https://www.home-assistant.io/components/haveibeenpwned", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hddtemp/manifest.json b/homeassistant/components/hddtemp/manifest.json new file mode 100644 index 00000000000000..2d34d3b4e7b645 --- /dev/null +++ b/homeassistant/components/hddtemp/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "hddtemp", + "name": "Hddtemp", + "documentation": "https://www.home-assistant.io/components/hddtemp", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hdmi_cec/manifest.json b/homeassistant/components/hdmi_cec/manifest.json new file mode 100644 index 00000000000000..b59d5622821db3 --- /dev/null +++ b/homeassistant/components/hdmi_cec/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hdmi_cec", + "name": "Hdmi cec", + "documentation": "https://www.home-assistant.io/components/hdmi_cec", + "requirements": [ + "pyCEC==0.4.13" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/heatmiser/manifest.json b/homeassistant/components/heatmiser/manifest.json new file mode 100644 index 00000000000000..0a11aecd079d9b --- /dev/null +++ b/homeassistant/components/heatmiser/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "heatmiser", + "name": "Heatmiser", + "documentation": "https://www.home-assistant.io/components/heatmiser", + "requirements": [ + "heatmiserV3==0.9.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/heos/manifest.json b/homeassistant/components/heos/manifest.json new file mode 100644 index 00000000000000..91cefed75f7d12 --- /dev/null +++ b/homeassistant/components/heos/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "heos", + "name": "Heos", + "documentation": "https://www.home-assistant.io/components/heos", + "requirements": [ + "pyheos==0.3.0" + ], + "dependencies": [], + "codeowners": [ + "@andrewsayre" + ] +} diff --git a/homeassistant/components/hikvision/manifest.json b/homeassistant/components/hikvision/manifest.json new file mode 100644 index 00000000000000..db6af975081c56 --- /dev/null +++ b/homeassistant/components/hikvision/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "hikvision", + "name": "Hikvision", + "documentation": "https://www.home-assistant.io/components/hikvision", + "requirements": [ + "pyhik==0.2.2" + ], + "dependencies": [], + "codeowners": [ + "@mezz64" + ] +} diff --git a/homeassistant/components/hikvisioncam/manifest.json b/homeassistant/components/hikvisioncam/manifest.json new file mode 100644 index 00000000000000..ec63425572df27 --- /dev/null +++ b/homeassistant/components/hikvisioncam/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hikvisioncam", + "name": "Hikvisioncam", + "documentation": "https://www.home-assistant.io/components/hikvisioncam", + "requirements": [ + "hikvision==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hipchat/manifest.json b/homeassistant/components/hipchat/manifest.json new file mode 100644 index 00000000000000..d49e05a5416f9c --- /dev/null +++ b/homeassistant/components/hipchat/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hipchat", + "name": "Hipchat", + "documentation": "https://www.home-assistant.io/components/hipchat", + "requirements": [ + "hipnotify==1.0.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/history/manifest.json b/homeassistant/components/history/manifest.json new file mode 100644 index 00000000000000..e0989958626a17 --- /dev/null +++ b/homeassistant/components/history/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "history", + "name": "History", + "documentation": "https://www.home-assistant.io/components/history", + "requirements": [], + "dependencies": [ + "http", + "recorder" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/history_graph/manifest.json b/homeassistant/components/history_graph/manifest.json new file mode 100644 index 00000000000000..fa0d437a700c96 --- /dev/null +++ b/homeassistant/components/history_graph/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "history_graph", + "name": "History graph", + "documentation": "https://www.home-assistant.io/components/history_graph", + "requirements": [], + "dependencies": [ + "history" + ], + "codeowners": [ + "@andrey-git" + ] +} diff --git a/homeassistant/components/history_stats/manifest.json b/homeassistant/components/history_stats/manifest.json new file mode 100644 index 00000000000000..8e0c1b249109da --- /dev/null +++ b/homeassistant/components/history_stats/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "history_stats", + "name": "History stats", + "documentation": "https://www.home-assistant.io/components/history_stats", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hitron_coda/manifest.json b/homeassistant/components/hitron_coda/manifest.json new file mode 100644 index 00000000000000..9f3c20fcca534b --- /dev/null +++ b/homeassistant/components/hitron_coda/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "hitron_coda", + "name": "Hitron coda", + "documentation": "https://www.home-assistant.io/components/hitron_coda", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hive/manifest.json b/homeassistant/components/hive/manifest.json new file mode 100644 index 00000000000000..76403f293ac0ec --- /dev/null +++ b/homeassistant/components/hive/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "hive", + "name": "Hive", + "documentation": "https://www.home-assistant.io/components/hive", + "requirements": [ + "pyhiveapi==0.2.17" + ], + "dependencies": [], + "codeowners": [ + "@Rendili", + "@KJonline" + ] +} diff --git a/homeassistant/components/hlk_sw16/manifest.json b/homeassistant/components/hlk_sw16/manifest.json new file mode 100644 index 00000000000000..5266b81ab0383d --- /dev/null +++ b/homeassistant/components/hlk_sw16/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hlk_sw16", + "name": "Hlk sw16", + "documentation": "https://www.home-assistant.io/components/hlk_sw16", + "requirements": [ + "hlk-sw16==0.0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/homeassistant/manifest.json b/homeassistant/components/homeassistant/manifest.json new file mode 100644 index 00000000000000..b612c3a9fa6454 --- /dev/null +++ b/homeassistant/components/homeassistant/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "homeassistant", + "name": "Home Assistant Core Integration", + "documentation": "https://www.home-assistant.io/components/homeassistant", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/homekit/manifest.json b/homeassistant/components/homekit/manifest.json new file mode 100644 index 00000000000000..fd781f206d1609 --- /dev/null +++ b/homeassistant/components/homekit/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "homekit", + "name": "Homekit", + "documentation": "https://www.home-assistant.io/components/homekit", + "requirements": [ + "HAP-python==2.4.2" + ], + "dependencies": [], + "codeowners": [ + "@cdce8p" + ] +} diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json new file mode 100644 index 00000000000000..e641f87e2a3472 --- /dev/null +++ b/homeassistant/components/homekit_controller/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "homekit_controller", + "name": "Homekit controller", + "documentation": "https://www.home-assistant.io/components/homekit_controller", + "requirements": [ + "homekit[IP]==0.13.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/homematic/manifest.json b/homeassistant/components/homematic/manifest.json new file mode 100644 index 00000000000000..cba29992f23f47 --- /dev/null +++ b/homeassistant/components/homematic/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "homematic", + "name": "Homematic", + "documentation": "https://www.home-assistant.io/components/homematic", + "requirements": [ + "pyhomematic==0.1.58" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json new file mode 100644 index 00000000000000..622928e8629663 --- /dev/null +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "homematicip_cloud", + "name": "Homematicip cloud", + "documentation": "https://www.home-assistant.io/components/homematicip_cloud", + "requirements": [ + "homematicip==0.10.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/homeworks/manifest.json b/homeassistant/components/homeworks/manifest.json new file mode 100644 index 00000000000000..cdbbffb8d3686f --- /dev/null +++ b/homeassistant/components/homeworks/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "homeworks", + "name": "Homeworks", + "documentation": "https://www.home-assistant.io/components/homeworks", + "requirements": [ + "pyhomeworks==0.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/honeywell/manifest.json b/homeassistant/components/honeywell/manifest.json new file mode 100644 index 00000000000000..c3d76703e91dd9 --- /dev/null +++ b/homeassistant/components/honeywell/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "honeywell", + "name": "Honeywell", + "documentation": "https://www.home-assistant.io/components/honeywell", + "requirements": [ + "evohomeclient==0.3.2", + "somecomfort==0.5.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hook/manifest.json b/homeassistant/components/hook/manifest.json new file mode 100644 index 00000000000000..d9898a71f8b717 --- /dev/null +++ b/homeassistant/components/hook/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "hook", + "name": "Hook", + "documentation": "https://www.home-assistant.io/components/hook", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/horizon/manifest.json b/homeassistant/components/horizon/manifest.json new file mode 100644 index 00000000000000..2916e81ce4f4e2 --- /dev/null +++ b/homeassistant/components/horizon/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "horizon", + "name": "Horizon", + "documentation": "https://www.home-assistant.io/components/horizon", + "requirements": [ + "horimote==0.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hp_ilo/manifest.json b/homeassistant/components/hp_ilo/manifest.json new file mode 100644 index 00000000000000..3df6632e47ab39 --- /dev/null +++ b/homeassistant/components/hp_ilo/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hp_ilo", + "name": "Hp ilo", + "documentation": "https://www.home-assistant.io/components/hp_ilo", + "requirements": [ + "python-hpilo==3.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/html5/manifest.json b/homeassistant/components/html5/manifest.json new file mode 100644 index 00000000000000..98b2834be7f804 --- /dev/null +++ b/homeassistant/components/html5/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "html5", + "name": "HTML5 Notifications", + "documentation": "https://www.home-assistant.io/components/html5", + "requirements": [ + "pywebpush==1.6.0" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/http/manifest.json b/homeassistant/components/http/manifest.json new file mode 100644 index 00000000000000..0bc5586445dd60 --- /dev/null +++ b/homeassistant/components/http/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "http", + "name": "HTTP", + "documentation": "https://www.home-assistant.io/components/http", + "requirements": [ + "aiohttp_cors==0.7.0" + ], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/htu21d/manifest.json b/homeassistant/components/htu21d/manifest.json new file mode 100644 index 00000000000000..70093df9b55fd2 --- /dev/null +++ b/homeassistant/components/htu21d/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "htu21d", + "name": "Htu21d", + "documentation": "https://www.home-assistant.io/components/htu21d", + "requirements": [ + "i2csense==0.0.4", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json new file mode 100644 index 00000000000000..2e096343b0921c --- /dev/null +++ b/homeassistant/components/huawei_lte/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "huawei_lte", + "name": "Huawei lte", + "documentation": "https://www.home-assistant.io/components/huawei_lte", + "requirements": [ + "huawei-lte-api==1.1.5" + ], + "dependencies": [], + "codeowners": [ + "@scop" + ] +} diff --git a/homeassistant/components/huawei_router/manifest.json b/homeassistant/components/huawei_router/manifest.json new file mode 100644 index 00000000000000..54fd155b557bd1 --- /dev/null +++ b/homeassistant/components/huawei_router/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "huawei_router", + "name": "Huawei router", + "documentation": "https://www.home-assistant.io/components/huawei_router", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@abmantis" + ] +} diff --git a/homeassistant/components/hue/manifest.json b/homeassistant/components/hue/manifest.json new file mode 100644 index 00000000000000..54a3a11a1899e2 --- /dev/null +++ b/homeassistant/components/hue/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "hue", + "name": "Philips Hue", + "documentation": "https://www.home-assistant.io/components/hue", + "requirements": [ + "aiohue==1.9.1" + ], + "dependencies": [], + "codeowners": [ + "@balloob" + ] +} diff --git a/homeassistant/components/hunterdouglas_powerview/manifest.json b/homeassistant/components/hunterdouglas_powerview/manifest.json new file mode 100644 index 00000000000000..c4e1bcc28e8534 --- /dev/null +++ b/homeassistant/components/hunterdouglas_powerview/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hunterdouglas_powerview", + "name": "Hunterdouglas powerview", + "documentation": "https://www.home-assistant.io/components/hunterdouglas_powerview", + "requirements": [ + "aiopvapi==1.6.14" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hydrawise/manifest.json b/homeassistant/components/hydrawise/manifest.json new file mode 100644 index 00000000000000..6d332a28bcc45d --- /dev/null +++ b/homeassistant/components/hydrawise/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hydrawise", + "name": "Hydrawise", + "documentation": "https://www.home-assistant.io/components/hydrawise", + "requirements": [ + "hydrawiser==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hydroquebec/manifest.json b/homeassistant/components/hydroquebec/manifest.json new file mode 100644 index 00000000000000..efea5ce0f2e0c2 --- /dev/null +++ b/homeassistant/components/hydroquebec/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "hydroquebec", + "name": "Hydroquebec", + "documentation": "https://www.home-assistant.io/components/hydroquebec", + "requirements": [ + "pyhydroquebec==2.2.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/hyperion/manifest.json b/homeassistant/components/hyperion/manifest.json new file mode 100644 index 00000000000000..980c227944a640 --- /dev/null +++ b/homeassistant/components/hyperion/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "hyperion", + "name": "Hyperion", + "documentation": "https://www.home-assistant.io/components/hyperion", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ialarm/manifest.json b/homeassistant/components/ialarm/manifest.json new file mode 100644 index 00000000000000..df492d136fd016 --- /dev/null +++ b/homeassistant/components/ialarm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ialarm", + "name": "Ialarm", + "documentation": "https://www.home-assistant.io/components/ialarm", + "requirements": [ + "pyialarm==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/icloud/manifest.json b/homeassistant/components/icloud/manifest.json new file mode 100644 index 00000000000000..865d64c6860b5a --- /dev/null +++ b/homeassistant/components/icloud/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "icloud", + "name": "Icloud", + "documentation": "https://www.home-assistant.io/components/icloud", + "requirements": [ + "pyicloud==0.9.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/idteck_prox/manifest.json b/homeassistant/components/idteck_prox/manifest.json new file mode 100644 index 00000000000000..8df144a0f8150b --- /dev/null +++ b/homeassistant/components/idteck_prox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "idteck_prox", + "name": "Idteck prox", + "documentation": "https://www.home-assistant.io/components/idteck_prox", + "requirements": [ + "rfk101py==0.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ifttt/manifest.json b/homeassistant/components/ifttt/manifest.json new file mode 100644 index 00000000000000..007e0870023e0d --- /dev/null +++ b/homeassistant/components/ifttt/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ifttt", + "name": "Ifttt", + "documentation": "https://www.home-assistant.io/components/ifttt", + "requirements": [ + "pyfttt==0.3" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/iglo/manifest.json b/homeassistant/components/iglo/manifest.json new file mode 100644 index 00000000000000..4d84c27cd93f82 --- /dev/null +++ b/homeassistant/components/iglo/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "iglo", + "name": "Iglo", + "documentation": "https://www.home-assistant.io/components/iglo", + "requirements": [ + "iglo==1.2.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ihc/manifest.json b/homeassistant/components/ihc/manifest.json new file mode 100644 index 00000000000000..bbcd4ab9389d3c --- /dev/null +++ b/homeassistant/components/ihc/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "ihc", + "name": "Ihc", + "documentation": "https://www.home-assistant.io/components/ihc", + "requirements": [ + "defusedxml==0.5.0", + "ihcsdk==2.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/image_processing/manifest.json b/homeassistant/components/image_processing/manifest.json new file mode 100644 index 00000000000000..e675d18a00b7d6 --- /dev/null +++ b/homeassistant/components/image_processing/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "image_processing", + "name": "Image processing", + "documentation": "https://www.home-assistant.io/components/image_processing", + "requirements": [], + "dependencies": [ + "camera" + ], + "codeowners": [] +} diff --git a/homeassistant/components/imap/manifest.json b/homeassistant/components/imap/manifest.json new file mode 100644 index 00000000000000..9e0f387a7a6ec6 --- /dev/null +++ b/homeassistant/components/imap/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "imap", + "name": "Imap", + "documentation": "https://www.home-assistant.io/components/imap", + "requirements": [ + "aioimaplib==0.7.15" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/imap_email_content/manifest.json b/homeassistant/components/imap_email_content/manifest.json new file mode 100644 index 00000000000000..a1e2c616832f2e --- /dev/null +++ b/homeassistant/components/imap_email_content/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "imap_email_content", + "name": "Imap email content", + "documentation": "https://www.home-assistant.io/components/imap_email_content", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/influxdb/manifest.json b/homeassistant/components/influxdb/manifest.json new file mode 100644 index 00000000000000..20652ddd046375 --- /dev/null +++ b/homeassistant/components/influxdb/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "influxdb", + "name": "Influxdb", + "documentation": "https://www.home-assistant.io/components/influxdb", + "requirements": [ + "influxdb==5.2.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/input_boolean/manifest.json b/homeassistant/components/input_boolean/manifest.json new file mode 100644 index 00000000000000..e233b5635fc77e --- /dev/null +++ b/homeassistant/components/input_boolean/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "input_boolean", + "name": "Input boolean", + "documentation": "https://www.home-assistant.io/components/input_boolean", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/input_datetime/manifest.json b/homeassistant/components/input_datetime/manifest.json new file mode 100644 index 00000000000000..287777e2ccf5a5 --- /dev/null +++ b/homeassistant/components/input_datetime/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "input_datetime", + "name": "Input datetime", + "documentation": "https://www.home-assistant.io/components/input_datetime", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/input_number/manifest.json b/homeassistant/components/input_number/manifest.json new file mode 100644 index 00000000000000..2015b8ea734f3d --- /dev/null +++ b/homeassistant/components/input_number/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "input_number", + "name": "Input number", + "documentation": "https://www.home-assistant.io/components/input_number", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/input_select/manifest.json b/homeassistant/components/input_select/manifest.json new file mode 100644 index 00000000000000..a71fb53a5d1b45 --- /dev/null +++ b/homeassistant/components/input_select/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "input_select", + "name": "Input select", + "documentation": "https://www.home-assistant.io/components/input_select", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/input_text/manifest.json b/homeassistant/components/input_text/manifest.json new file mode 100644 index 00000000000000..6362e6793192f3 --- /dev/null +++ b/homeassistant/components/input_text/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "input_text", + "name": "Input text", + "documentation": "https://www.home-assistant.io/components/input_text", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json new file mode 100644 index 00000000000000..7ba27cbe625fc1 --- /dev/null +++ b/homeassistant/components/insteon/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "insteon", + "name": "Insteon", + "documentation": "https://www.home-assistant.io/components/insteon", + "requirements": [ + "insteonplm==0.15.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/insteon_local/manifest.json b/homeassistant/components/insteon_local/manifest.json new file mode 100644 index 00000000000000..64b6bccdba63c0 --- /dev/null +++ b/homeassistant/components/insteon_local/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "insteon_local", + "name": "Insteon local", + "documentation": "https://www.home-assistant.io/components/insteon_local", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/insteon_plm/manifest.json b/homeassistant/components/insteon_plm/manifest.json new file mode 100644 index 00000000000000..fa382dd2df020e --- /dev/null +++ b/homeassistant/components/insteon_plm/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "insteon_plm", + "name": "Insteon plm", + "documentation": "https://www.home-assistant.io/components/insteon_plm", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/integration/manifest.json b/homeassistant/components/integration/manifest.json new file mode 100644 index 00000000000000..869ad2766f90bc --- /dev/null +++ b/homeassistant/components/integration/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "integration", + "name": "Integration", + "documentation": "https://www.home-assistant.io/components/integration", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/intent_script/manifest.json b/homeassistant/components/intent_script/manifest.json new file mode 100644 index 00000000000000..891be6b21802ed --- /dev/null +++ b/homeassistant/components/intent_script/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "intent_script", + "name": "Intent script", + "documentation": "https://www.home-assistant.io/components/intent_script", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/introduction/manifest.json b/homeassistant/components/introduction/manifest.json new file mode 100644 index 00000000000000..4caa31e34e6127 --- /dev/null +++ b/homeassistant/components/introduction/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "introduction", + "name": "Introduction", + "documentation": "https://www.home-assistant.io/components/introduction", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/ios/manifest.json b/homeassistant/components/ios/manifest.json new file mode 100644 index 00000000000000..97c2e2ae28f3c4 --- /dev/null +++ b/homeassistant/components/ios/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "ios", + "name": "Ios", + "documentation": "https://www.home-assistant.io/components/ios", + "requirements": [], + "dependencies": [ + "device_tracker", + "http", + "zeroconf" + ], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/iota/manifest.json b/homeassistant/components/iota/manifest.json new file mode 100644 index 00000000000000..d83defbbec3332 --- /dev/null +++ b/homeassistant/components/iota/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "iota", + "name": "Iota", + "documentation": "https://www.home-assistant.io/components/iota", + "requirements": [ + "pyota==2.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/iperf3/manifest.json b/homeassistant/components/iperf3/manifest.json new file mode 100644 index 00000000000000..e35be24fc8089f --- /dev/null +++ b/homeassistant/components/iperf3/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "iperf3", + "name": "Iperf3", + "documentation": "https://www.home-assistant.io/components/iperf3", + "requirements": [ + "iperf3==0.1.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ipma/manifest.json b/homeassistant/components/ipma/manifest.json new file mode 100644 index 00000000000000..29fc0429e86d85 --- /dev/null +++ b/homeassistant/components/ipma/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ipma", + "name": "Ipma", + "documentation": "https://www.home-assistant.io/components/ipma", + "requirements": [ + "pyipma==1.2.1" + ], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/irish_rail_transport/manifest.json b/homeassistant/components/irish_rail_transport/manifest.json new file mode 100644 index 00000000000000..5961400e68ec8c --- /dev/null +++ b/homeassistant/components/irish_rail_transport/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "irish_rail_transport", + "name": "Irish rail transport", + "documentation": "https://www.home-assistant.io/components/irish_rail_transport", + "requirements": [ + "pyirishrail==0.0.2" + ], + "dependencies": [], + "codeowners": [ + "@ttroy50" + ] +} diff --git a/homeassistant/components/islamic_prayer_times/manifest.json b/homeassistant/components/islamic_prayer_times/manifest.json new file mode 100644 index 00000000000000..4dc9e2cb7c3f53 --- /dev/null +++ b/homeassistant/components/islamic_prayer_times/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "islamic_prayer_times", + "name": "Islamic prayer times", + "documentation": "https://www.home-assistant.io/components/islamic_prayer_times", + "requirements": [ + "prayer_times_calculator==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/iss/manifest.json b/homeassistant/components/iss/manifest.json new file mode 100644 index 00000000000000..dc71e81ac0808d --- /dev/null +++ b/homeassistant/components/iss/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "iss", + "name": "Iss", + "documentation": "https://www.home-assistant.io/components/iss", + "requirements": [ + "pyiss==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/isy994/manifest.json b/homeassistant/components/isy994/manifest.json new file mode 100644 index 00000000000000..7860c080b2fe05 --- /dev/null +++ b/homeassistant/components/isy994/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "isy994", + "name": "Isy994", + "documentation": "https://www.home-assistant.io/components/isy994", + "requirements": [ + "PyISY==1.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/itach/manifest.json b/homeassistant/components/itach/manifest.json new file mode 100644 index 00000000000000..c26b19c636e59c --- /dev/null +++ b/homeassistant/components/itach/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "itach", + "name": "Itach", + "documentation": "https://www.home-assistant.io/components/itach", + "requirements": [ + "pyitachip2ir==0.0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/itunes/manifest.json b/homeassistant/components/itunes/manifest.json new file mode 100644 index 00000000000000..6f05125661e2c8 --- /dev/null +++ b/homeassistant/components/itunes/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "itunes", + "name": "Itunes", + "documentation": "https://www.home-assistant.io/components/itunes", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/jewish_calendar/manifest.json b/homeassistant/components/jewish_calendar/manifest.json new file mode 100644 index 00000000000000..1f2917865b3454 --- /dev/null +++ b/homeassistant/components/jewish_calendar/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "jewish_calendar", + "name": "Jewish calendar", + "documentation": "https://www.home-assistant.io/components/jewish_calendar", + "requirements": [ + "hdate==0.8.7" + ], + "dependencies": [], + "codeowners": [ + "@tsvi" + ] +} diff --git a/homeassistant/components/joaoapps_join/manifest.json b/homeassistant/components/joaoapps_join/manifest.json new file mode 100644 index 00000000000000..220f2af2035552 --- /dev/null +++ b/homeassistant/components/joaoapps_join/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "joaoapps_join", + "name": "Joaoapps join", + "documentation": "https://www.home-assistant.io/components/joaoapps_join", + "requirements": [ + "python-join-api==0.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/juicenet/manifest.json b/homeassistant/components/juicenet/manifest.json new file mode 100644 index 00000000000000..e65aab2b69da28 --- /dev/null +++ b/homeassistant/components/juicenet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "juicenet", + "name": "Juicenet", + "documentation": "https://www.home-assistant.io/components/juicenet", + "requirements": [ + "python-juicenet==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/kankun/manifest.json b/homeassistant/components/kankun/manifest.json new file mode 100644 index 00000000000000..8e4e9747901e68 --- /dev/null +++ b/homeassistant/components/kankun/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "kankun", + "name": "Kankun", + "documentation": "https://www.home-assistant.io/components/kankun", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/keenetic_ndms2/manifest.json b/homeassistant/components/keenetic_ndms2/manifest.json new file mode 100644 index 00000000000000..d95e6384606b86 --- /dev/null +++ b/homeassistant/components/keenetic_ndms2/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "keenetic_ndms2", + "name": "Keenetic ndms2", + "documentation": "https://www.home-assistant.io/components/keenetic_ndms2", + "requirements": [ + "ndms2_client==0.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/keyboard/manifest.json b/homeassistant/components/keyboard/manifest.json new file mode 100644 index 00000000000000..0e8ade339c2104 --- /dev/null +++ b/homeassistant/components/keyboard/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "keyboard", + "name": "Keyboard", + "documentation": "https://www.home-assistant.io/components/keyboard", + "requirements": [ + "pyuserinput==0.1.11" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/keyboard_remote/manifest.json b/homeassistant/components/keyboard_remote/manifest.json new file mode 100644 index 00000000000000..d87d1abca4831d --- /dev/null +++ b/homeassistant/components/keyboard_remote/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "keyboard_remote", + "name": "Keyboard remote", + "documentation": "https://www.home-assistant.io/components/keyboard_remote", + "requirements": [ + "evdev==0.6.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/kira/manifest.json b/homeassistant/components/kira/manifest.json new file mode 100644 index 00000000000000..b7edd1f6c5f05d --- /dev/null +++ b/homeassistant/components/kira/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "kira", + "name": "Kira", + "documentation": "https://www.home-assistant.io/components/kira", + "requirements": [ + "pykira==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/kiwi/manifest.json b/homeassistant/components/kiwi/manifest.json new file mode 100644 index 00000000000000..9f1595ebd77245 --- /dev/null +++ b/homeassistant/components/kiwi/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "kiwi", + "name": "Kiwi", + "documentation": "https://www.home-assistant.io/components/kiwi", + "requirements": [ + "kiwiki-client==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json new file mode 100644 index 00000000000000..1b1f16ccb039d2 --- /dev/null +++ b/homeassistant/components/knx/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "knx", + "name": "Knx", + "documentation": "https://www.home-assistant.io/components/knx", + "requirements": [ + "xknx==0.10.0" + ], + "dependencies": [], + "codeowners": [ + "@Julius2342" + ] +} diff --git a/homeassistant/components/kodi/manifest.json b/homeassistant/components/kodi/manifest.json new file mode 100644 index 00000000000000..8c684d495e91db --- /dev/null +++ b/homeassistant/components/kodi/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "kodi", + "name": "Kodi", + "documentation": "https://www.home-assistant.io/components/kodi", + "requirements": [ + "jsonrpc-async==0.6", + "jsonrpc-websocket==0.6" + ], + "dependencies": [], + "codeowners": [ + "@armills" + ] +} diff --git a/homeassistant/components/konnected/manifest.json b/homeassistant/components/konnected/manifest.json new file mode 100644 index 00000000000000..e4129af39bd10a --- /dev/null +++ b/homeassistant/components/konnected/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "konnected", + "name": "Konnected", + "documentation": "https://www.home-assistant.io/components/konnected", + "requirements": [ + "konnected==0.1.5" + ], + "dependencies": [ + "http" + ], + "codeowners": [ + "@heythisisnate" + ] +} diff --git a/homeassistant/components/kwb/manifest.json b/homeassistant/components/kwb/manifest.json new file mode 100644 index 00000000000000..783907c02202e4 --- /dev/null +++ b/homeassistant/components/kwb/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "kwb", + "name": "Kwb", + "documentation": "https://www.home-assistant.io/components/kwb", + "requirements": [ + "pykwb==0.0.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lacrosse/manifest.json b/homeassistant/components/lacrosse/manifest.json new file mode 100644 index 00000000000000..4716b3cb548e67 --- /dev/null +++ b/homeassistant/components/lacrosse/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lacrosse", + "name": "Lacrosse", + "documentation": "https://www.home-assistant.io/components/lacrosse", + "requirements": [ + "pylacrosse==0.3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lametric/manifest.json b/homeassistant/components/lametric/manifest.json new file mode 100644 index 00000000000000..bbf22918a75545 --- /dev/null +++ b/homeassistant/components/lametric/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "lametric", + "name": "Lametric", + "documentation": "https://www.home-assistant.io/components/lametric", + "requirements": [ + "lmnotify==0.0.4" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/lannouncer/manifest.json b/homeassistant/components/lannouncer/manifest.json new file mode 100644 index 00000000000000..951dd3ff85b5e2 --- /dev/null +++ b/homeassistant/components/lannouncer/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "lannouncer", + "name": "Lannouncer", + "documentation": "https://www.home-assistant.io/components/lannouncer", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lastfm/manifest.json b/homeassistant/components/lastfm/manifest.json new file mode 100644 index 00000000000000..2617b3e206bea8 --- /dev/null +++ b/homeassistant/components/lastfm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lastfm", + "name": "Lastfm", + "documentation": "https://www.home-assistant.io/components/lastfm", + "requirements": [ + "pylast==3.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/launch_library/manifest.json b/homeassistant/components/launch_library/manifest.json new file mode 100644 index 00000000000000..bbe9fa8ad054ce --- /dev/null +++ b/homeassistant/components/launch_library/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "launch_library", + "name": "Launch library", + "documentation": "https://www.home-assistant.io/components/launch_library", + "requirements": [ + "pylaunches==0.2.0" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/lcn/manifest.json b/homeassistant/components/lcn/manifest.json new file mode 100644 index 00000000000000..bbf2746c0670f0 --- /dev/null +++ b/homeassistant/components/lcn/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lcn", + "name": "Lcn", + "documentation": "https://www.home-assistant.io/components/lcn", + "requirements": [ + "pypck==0.5.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lg_netcast/manifest.json b/homeassistant/components/lg_netcast/manifest.json new file mode 100644 index 00000000000000..1728aa50614656 --- /dev/null +++ b/homeassistant/components/lg_netcast/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lg_netcast", + "name": "Lg netcast", + "documentation": "https://www.home-assistant.io/components/lg_netcast", + "requirements": [ + "pylgnetcast-homeassistant==0.2.0.dev0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lg_soundbar/manifest.json b/homeassistant/components/lg_soundbar/manifest.json new file mode 100644 index 00000000000000..b09c8809382a7d --- /dev/null +++ b/homeassistant/components/lg_soundbar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lg_soundbar", + "name": "Lg soundbar", + "documentation": "https://www.home-assistant.io/components/lg_soundbar", + "requirements": [ + "temescal==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lifx/manifest.json b/homeassistant/components/lifx/manifest.json new file mode 100644 index 00000000000000..6b811b01f514e4 --- /dev/null +++ b/homeassistant/components/lifx/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "lifx", + "name": "Lifx", + "documentation": "https://www.home-assistant.io/components/lifx", + "requirements": [ + "aiolifx==0.6.7", + "aiolifx_effects==0.2.1" + ], + "dependencies": [], + "codeowners": [ + "@amelchio" + ] +} diff --git a/homeassistant/components/lifx_cloud/manifest.json b/homeassistant/components/lifx_cloud/manifest.json new file mode 100644 index 00000000000000..c2834fbc788b63 --- /dev/null +++ b/homeassistant/components/lifx_cloud/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lifx_cloud", + "name": "Lifx cloud", + "documentation": "https://www.home-assistant.io/components/lifx_cloud", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@amelchio" + ] +} diff --git a/homeassistant/components/lifx_legacy/manifest.json b/homeassistant/components/lifx_legacy/manifest.json new file mode 100644 index 00000000000000..4ff59ac17703df --- /dev/null +++ b/homeassistant/components/lifx_legacy/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "lifx_legacy", + "name": "Lifx legacy", + "documentation": "https://www.home-assistant.io/components/lifx_legacy", + "requirements": [ + "liffylights==0.9.4" + ], + "dependencies": [], + "codeowners": [ + "@amelchio" + ] +} diff --git a/homeassistant/components/light/manifest.json b/homeassistant/components/light/manifest.json new file mode 100644 index 00000000000000..62eb96967f5ec5 --- /dev/null +++ b/homeassistant/components/light/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "light", + "name": "Light", + "documentation": "https://www.home-assistant.io/components/light", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/lightwave/manifest.json b/homeassistant/components/lightwave/manifest.json new file mode 100644 index 00000000000000..a26500f69a6e25 --- /dev/null +++ b/homeassistant/components/lightwave/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lightwave", + "name": "Lightwave", + "documentation": "https://www.home-assistant.io/components/lightwave", + "requirements": [ + "lightwave==0.15" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/limitlessled/manifest.json b/homeassistant/components/limitlessled/manifest.json new file mode 100644 index 00000000000000..f8b42fabcbe12b --- /dev/null +++ b/homeassistant/components/limitlessled/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "limitlessled", + "name": "Limitlessled", + "documentation": "https://www.home-assistant.io/components/limitlessled", + "requirements": [ + "limitlessled==1.1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/linksys_ap/manifest.json b/homeassistant/components/linksys_ap/manifest.json new file mode 100644 index 00000000000000..ccad7298d6b40e --- /dev/null +++ b/homeassistant/components/linksys_ap/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "linksys_ap", + "name": "Linksys ap", + "documentation": "https://www.home-assistant.io/components/linksys_ap", + "requirements": [ + "beautifulsoup4==4.7.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/linksys_smart/manifest.json b/homeassistant/components/linksys_smart/manifest.json new file mode 100644 index 00000000000000..19bb079c29cef3 --- /dev/null +++ b/homeassistant/components/linksys_smart/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "linksys_smart", + "name": "Linksys smart", + "documentation": "https://www.home-assistant.io/components/linksys_smart", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/linky/manifest.json b/homeassistant/components/linky/manifest.json new file mode 100644 index 00000000000000..706962b5c4d368 --- /dev/null +++ b/homeassistant/components/linky/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "linky", + "name": "Linky", + "documentation": "https://www.home-assistant.io/components/linky", + "requirements": [ + "pylinky==0.3.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/linode/manifest.json b/homeassistant/components/linode/manifest.json new file mode 100644 index 00000000000000..7dc2e0d7518e69 --- /dev/null +++ b/homeassistant/components/linode/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "linode", + "name": "Linode", + "documentation": "https://www.home-assistant.io/components/linode", + "requirements": [ + "linode-api==4.1.9b1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/linux_battery/manifest.json b/homeassistant/components/linux_battery/manifest.json new file mode 100644 index 00000000000000..4c32b88b2d5b7e --- /dev/null +++ b/homeassistant/components/linux_battery/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "linux_battery", + "name": "Linux battery", + "documentation": "https://www.home-assistant.io/components/linux_battery", + "requirements": [ + "batinfo==0.4.2" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/lirc/manifest.json b/homeassistant/components/lirc/manifest.json new file mode 100644 index 00000000000000..d11cf0b2f1ef71 --- /dev/null +++ b/homeassistant/components/lirc/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lirc", + "name": "Lirc", + "documentation": "https://www.home-assistant.io/components/lirc", + "requirements": [ + "python-lirc==1.2.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/litejet/manifest.json b/homeassistant/components/litejet/manifest.json new file mode 100644 index 00000000000000..08bcac67903088 --- /dev/null +++ b/homeassistant/components/litejet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "litejet", + "name": "Litejet", + "documentation": "https://www.home-assistant.io/components/litejet", + "requirements": [ + "pylitejet==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/liveboxplaytv/manifest.json b/homeassistant/components/liveboxplaytv/manifest.json new file mode 100644 index 00000000000000..863507ada6c9e5 --- /dev/null +++ b/homeassistant/components/liveboxplaytv/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "liveboxplaytv", + "name": "Liveboxplaytv", + "documentation": "https://www.home-assistant.io/components/liveboxplaytv", + "requirements": [ + "liveboxplaytv==2.0.2", + "pyteleloisirs==3.4" + ], + "dependencies": [], + "codeowners": [ + "@pschmitt" + ] +} diff --git a/homeassistant/components/llamalab_automate/manifest.json b/homeassistant/components/llamalab_automate/manifest.json new file mode 100644 index 00000000000000..e66050fceb5728 --- /dev/null +++ b/homeassistant/components/llamalab_automate/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "llamalab_automate", + "name": "Llamalab automate", + "documentation": "https://www.home-assistant.io/components/llamalab_automate", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/local_file/manifest.json b/homeassistant/components/local_file/manifest.json new file mode 100644 index 00000000000000..14a503f33f571d --- /dev/null +++ b/homeassistant/components/local_file/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "local_file", + "name": "Local file", + "documentation": "https://www.home-assistant.io/components/local_file", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/locative/manifest.json b/homeassistant/components/locative/manifest.json new file mode 100644 index 00000000000000..afe2850caf827d --- /dev/null +++ b/homeassistant/components/locative/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "locative", + "name": "Locative", + "documentation": "https://www.home-assistant.io/components/locative", + "requirements": [], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/lock/manifest.json b/homeassistant/components/lock/manifest.json new file mode 100644 index 00000000000000..29a7a5513d0854 --- /dev/null +++ b/homeassistant/components/lock/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lock", + "name": "Lock", + "documentation": "https://www.home-assistant.io/components/lock", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/lockitron/manifest.json b/homeassistant/components/lockitron/manifest.json new file mode 100644 index 00000000000000..b515d65a14fda4 --- /dev/null +++ b/homeassistant/components/lockitron/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "lockitron", + "name": "Lockitron", + "documentation": "https://www.home-assistant.io/components/lockitron", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/logbook/manifest.json b/homeassistant/components/logbook/manifest.json new file mode 100644 index 00000000000000..cedce8152a294b --- /dev/null +++ b/homeassistant/components/logbook/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "logbook", + "name": "Logbook", + "documentation": "https://www.home-assistant.io/components/logbook", + "requirements": [], + "dependencies": [ + "frontend", + "recorder" + ], + "codeowners": [] +} diff --git a/homeassistant/components/logentries/manifest.json b/homeassistant/components/logentries/manifest.json new file mode 100644 index 00000000000000..60be8f275eef60 --- /dev/null +++ b/homeassistant/components/logentries/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "logentries", + "name": "Logentries", + "documentation": "https://www.home-assistant.io/components/logentries", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/logger/manifest.json b/homeassistant/components/logger/manifest.json new file mode 100644 index 00000000000000..c6b6238703982d --- /dev/null +++ b/homeassistant/components/logger/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "logger", + "name": "Logger", + "documentation": "https://www.home-assistant.io/components/logger", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/logi_circle/manifest.json b/homeassistant/components/logi_circle/manifest.json new file mode 100644 index 00000000000000..3e8281d5a9cace --- /dev/null +++ b/homeassistant/components/logi_circle/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "logi_circle", + "name": "Logi circle", + "documentation": "https://www.home-assistant.io/components/logi_circle", + "requirements": [ + "logi_circle==0.1.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/london_air/manifest.json b/homeassistant/components/london_air/manifest.json new file mode 100644 index 00000000000000..3f0c97edfe012b --- /dev/null +++ b/homeassistant/components/london_air/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "london_air", + "name": "London air", + "documentation": "https://www.home-assistant.io/components/london_air", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/london_underground/manifest.json b/homeassistant/components/london_underground/manifest.json new file mode 100644 index 00000000000000..5262fa4837ea9a --- /dev/null +++ b/homeassistant/components/london_underground/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "london_underground", + "name": "London underground", + "documentation": "https://www.home-assistant.io/components/london_underground", + "requirements": [ + "london-tube-status==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/loopenergy/manifest.json b/homeassistant/components/loopenergy/manifest.json new file mode 100644 index 00000000000000..b282755b1a0b1c --- /dev/null +++ b/homeassistant/components/loopenergy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "loopenergy", + "name": "Loopenergy", + "documentation": "https://www.home-assistant.io/components/loopenergy", + "requirements": [ + "pyloopenergy==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lovelace/manifest.json b/homeassistant/components/lovelace/manifest.json new file mode 100644 index 00000000000000..1c1a7a107e4fb0 --- /dev/null +++ b/homeassistant/components/lovelace/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lovelace", + "name": "Lovelace", + "documentation": "https://www.home-assistant.io/components/lovelace", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/luci/manifest.json b/homeassistant/components/luci/manifest.json new file mode 100644 index 00000000000000..46e1702c36e672 --- /dev/null +++ b/homeassistant/components/luci/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "luci", + "name": "Luci", + "documentation": "https://www.home-assistant.io/components/luci", + "requirements": [ + "openwrt-luci-rpc==1.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/luftdaten/manifest.json b/homeassistant/components/luftdaten/manifest.json new file mode 100644 index 00000000000000..0e6a46a5c5de04 --- /dev/null +++ b/homeassistant/components/luftdaten/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "luftdaten", + "name": "Luftdaten", + "documentation": "https://www.home-assistant.io/components/luftdaten", + "requirements": [ + "luftdaten==0.3.4" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/lupusec/manifest.json b/homeassistant/components/lupusec/manifest.json new file mode 100644 index 00000000000000..344ec82d976cb0 --- /dev/null +++ b/homeassistant/components/lupusec/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lupusec", + "name": "Lupusec", + "documentation": "https://www.home-assistant.io/components/lupusec", + "requirements": [ + "lupupy==0.0.17" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lutron/manifest.json b/homeassistant/components/lutron/manifest.json new file mode 100644 index 00000000000000..b536eef02854bd --- /dev/null +++ b/homeassistant/components/lutron/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lutron", + "name": "Lutron", + "documentation": "https://www.home-assistant.io/components/lutron", + "requirements": [ + "pylutron==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lutron_caseta/manifest.json b/homeassistant/components/lutron_caseta/manifest.json new file mode 100644 index 00000000000000..4da58cdfc40274 --- /dev/null +++ b/homeassistant/components/lutron_caseta/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lutron_caseta", + "name": "Lutron caseta", + "documentation": "https://www.home-assistant.io/components/lutron_caseta", + "requirements": [ + "pylutron-caseta==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lw12wifi/manifest.json b/homeassistant/components/lw12wifi/manifest.json new file mode 100644 index 00000000000000..205072055bbe22 --- /dev/null +++ b/homeassistant/components/lw12wifi/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lw12wifi", + "name": "Lw12wifi", + "documentation": "https://www.home-assistant.io/components/lw12wifi", + "requirements": [ + "lw12==0.9.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/lyft/manifest.json b/homeassistant/components/lyft/manifest.json new file mode 100644 index 00000000000000..ff7da7190d94e6 --- /dev/null +++ b/homeassistant/components/lyft/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "lyft", + "name": "Lyft", + "documentation": "https://www.home-assistant.io/components/lyft", + "requirements": [ + "lyft_rides==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/magicseaweed/manifest.json b/homeassistant/components/magicseaweed/manifest.json new file mode 100644 index 00000000000000..6534d927f1b872 --- /dev/null +++ b/homeassistant/components/magicseaweed/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "magicseaweed", + "name": "Magicseaweed", + "documentation": "https://www.home-assistant.io/components/magicseaweed", + "requirements": [ + "magicseaweed==1.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mailbox/manifest.json b/homeassistant/components/mailbox/manifest.json new file mode 100644 index 00000000000000..4ca1db564a4cf0 --- /dev/null +++ b/homeassistant/components/mailbox/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mailbox", + "name": "Mailbox", + "documentation": "https://www.home-assistant.io/components/mailbox", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/mailgun/manifest.json b/homeassistant/components/mailgun/manifest.json new file mode 100644 index 00000000000000..2979b391ec29d5 --- /dev/null +++ b/homeassistant/components/mailgun/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mailgun", + "name": "Mailgun", + "documentation": "https://www.home-assistant.io/components/mailgun", + "requirements": [ + "pymailgunner==1.4" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/manual/manifest.json b/homeassistant/components/manual/manifest.json new file mode 100644 index 00000000000000..6c788971629ea8 --- /dev/null +++ b/homeassistant/components/manual/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "manual", + "name": "Manual", + "documentation": "https://www.home-assistant.io/components/manual", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/manual_mqtt/manifest.json b/homeassistant/components/manual_mqtt/manifest.json new file mode 100644 index 00000000000000..cc467ade5c1020 --- /dev/null +++ b/homeassistant/components/manual_mqtt/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "manual_mqtt", + "name": "Manual mqtt", + "documentation": "https://www.home-assistant.io/components/manual_mqtt", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/map/manifest.json b/homeassistant/components/map/manifest.json new file mode 100644 index 00000000000000..993dfc6577eaba --- /dev/null +++ b/homeassistant/components/map/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "map", + "name": "Map", + "documentation": "https://www.home-assistant.io/components/map", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/marytts/manifest.json b/homeassistant/components/marytts/manifest.json new file mode 100644 index 00000000000000..5316935c442db6 --- /dev/null +++ b/homeassistant/components/marytts/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "marytts", + "name": "Marytts", + "documentation": "https://www.home-assistant.io/components/marytts", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mastodon/manifest.json b/homeassistant/components/mastodon/manifest.json new file mode 100644 index 00000000000000..fd7e023fc913b5 --- /dev/null +++ b/homeassistant/components/mastodon/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mastodon", + "name": "Mastodon", + "documentation": "https://www.home-assistant.io/components/mastodon", + "requirements": [ + "Mastodon.py==1.3.1" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/matrix/manifest.json b/homeassistant/components/matrix/manifest.json new file mode 100644 index 00000000000000..9ea1a6f0c5558c --- /dev/null +++ b/homeassistant/components/matrix/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "matrix", + "name": "Matrix", + "documentation": "https://www.home-assistant.io/components/matrix", + "requirements": [ + "matrix-client==0.2.0" + ], + "dependencies": [], + "codeowners": [ + "@tinloaf" + ] +} diff --git a/homeassistant/components/maxcube/manifest.json b/homeassistant/components/maxcube/manifest.json new file mode 100644 index 00000000000000..a28096c5eb7767 --- /dev/null +++ b/homeassistant/components/maxcube/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "maxcube", + "name": "Maxcube", + "documentation": "https://www.home-assistant.io/components/maxcube", + "requirements": [ + "maxcube-api==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/media_extractor/manifest.json b/homeassistant/components/media_extractor/manifest.json new file mode 100644 index 00000000000000..53375f14bfedbd --- /dev/null +++ b/homeassistant/components/media_extractor/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "media_extractor", + "name": "Media extractor", + "documentation": "https://www.home-assistant.io/components/media_extractor", + "requirements": [ + "youtube_dl==2019.03.18" + ], + "dependencies": [ + "media_player" + ], + "codeowners": [] +} diff --git a/homeassistant/components/media_player/manifest.json b/homeassistant/components/media_player/manifest.json new file mode 100644 index 00000000000000..bf6f8fabafa43c --- /dev/null +++ b/homeassistant/components/media_player/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "media_player", + "name": "Media player", + "documentation": "https://www.home-assistant.io/components/media_player", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/mediaroom/manifest.json b/homeassistant/components/mediaroom/manifest.json new file mode 100644 index 00000000000000..134d85fa1712d2 --- /dev/null +++ b/homeassistant/components/mediaroom/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mediaroom", + "name": "Mediaroom", + "documentation": "https://www.home-assistant.io/components/mediaroom", + "requirements": [ + "pymediaroom==0.6.4" + ], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/melissa/manifest.json b/homeassistant/components/melissa/manifest.json new file mode 100644 index 00000000000000..f9fa1cab502cdb --- /dev/null +++ b/homeassistant/components/melissa/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "melissa", + "name": "Melissa", + "documentation": "https://www.home-assistant.io/components/melissa", + "requirements": [ + "py-melissa-climate==2.0.0" + ], + "dependencies": [], + "codeowners": [ + "@kennedyshead" + ] +} diff --git a/homeassistant/components/meraki/manifest.json b/homeassistant/components/meraki/manifest.json new file mode 100644 index 00000000000000..d17e7c60525eab --- /dev/null +++ b/homeassistant/components/meraki/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "meraki", + "name": "Meraki", + "documentation": "https://www.home-assistant.io/components/meraki", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/message_bird/manifest.json b/homeassistant/components/message_bird/manifest.json new file mode 100644 index 00000000000000..a6c49b3c39688f --- /dev/null +++ b/homeassistant/components/message_bird/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "message_bird", + "name": "Message bird", + "documentation": "https://www.home-assistant.io/components/message_bird", + "requirements": [ + "messagebird==1.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/met/manifest.json b/homeassistant/components/met/manifest.json new file mode 100644 index 00000000000000..b2ef166be50194 --- /dev/null +++ b/homeassistant/components/met/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "met", + "name": "Met", + "documentation": "https://www.home-assistant.io/components/met", + "requirements": [ + "pyMetno==0.4.6" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/meteo_france/manifest.json b/homeassistant/components/meteo_france/manifest.json new file mode 100644 index 00000000000000..20ad5e46fe628b --- /dev/null +++ b/homeassistant/components/meteo_france/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "meteo_france", + "name": "Meteo france", + "documentation": "https://www.home-assistant.io/components/meteo_france", + "requirements": [ + "meteofrance==0.3.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/metoffice/manifest.json b/homeassistant/components/metoffice/manifest.json new file mode 100644 index 00000000000000..f5d358854f6f79 --- /dev/null +++ b/homeassistant/components/metoffice/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "metoffice", + "name": "Metoffice", + "documentation": "https://www.home-assistant.io/components/metoffice", + "requirements": [ + "datapoint==0.4.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mfi/manifest.json b/homeassistant/components/mfi/manifest.json new file mode 100644 index 00000000000000..1e84b39a366e4f --- /dev/null +++ b/homeassistant/components/mfi/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mfi", + "name": "Mfi", + "documentation": "https://www.home-assistant.io/components/mfi", + "requirements": [ + "mficlient==0.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mhz19/manifest.json b/homeassistant/components/mhz19/manifest.json new file mode 100644 index 00000000000000..8545db90e27583 --- /dev/null +++ b/homeassistant/components/mhz19/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mhz19", + "name": "Mhz19", + "documentation": "https://www.home-assistant.io/components/mhz19", + "requirements": [ + "pmsensor==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/microsoft/manifest.json b/homeassistant/components/microsoft/manifest.json new file mode 100644 index 00000000000000..827d961a093859 --- /dev/null +++ b/homeassistant/components/microsoft/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "microsoft", + "name": "Microsoft", + "documentation": "https://www.home-assistant.io/components/microsoft", + "requirements": [ + "pycsspeechtts==1.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/microsoft_face/manifest.json b/homeassistant/components/microsoft_face/manifest.json new file mode 100644 index 00000000000000..7f6c4fbd935759 --- /dev/null +++ b/homeassistant/components/microsoft_face/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "microsoft_face", + "name": "Microsoft face", + "documentation": "https://www.home-assistant.io/components/microsoft_face", + "requirements": [], + "dependencies": [ + "camera" + ], + "codeowners": [] +} diff --git a/homeassistant/components/microsoft_face_detect/manifest.json b/homeassistant/components/microsoft_face_detect/manifest.json new file mode 100644 index 00000000000000..955b67a0a76be5 --- /dev/null +++ b/homeassistant/components/microsoft_face_detect/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "microsoft_face_detect", + "name": "Microsoft face detect", + "documentation": "https://www.home-assistant.io/components/microsoft_face_detect", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/microsoft_face_identify/manifest.json b/homeassistant/components/microsoft_face_identify/manifest.json new file mode 100644 index 00000000000000..f32b9220b3da55 --- /dev/null +++ b/homeassistant/components/microsoft_face_identify/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "microsoft_face_identify", + "name": "Microsoft face identify", + "documentation": "https://www.home-assistant.io/components/microsoft_face_identify", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/miflora/manifest.json b/homeassistant/components/miflora/manifest.json new file mode 100644 index 00000000000000..d4e7a333acf287 --- /dev/null +++ b/homeassistant/components/miflora/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "miflora", + "name": "Miflora", + "documentation": "https://www.home-assistant.io/components/miflora", + "requirements": [ + "miflora==0.4.0" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen", + "@ChristianKuehnel" + ] +} diff --git a/homeassistant/components/mikrotik/manifest.json b/homeassistant/components/mikrotik/manifest.json new file mode 100644 index 00000000000000..caa9733f241841 --- /dev/null +++ b/homeassistant/components/mikrotik/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mikrotik", + "name": "Mikrotik", + "documentation": "https://www.home-assistant.io/components/mikrotik", + "requirements": [ + "librouteros==2.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mill/manifest.json b/homeassistant/components/mill/manifest.json new file mode 100644 index 00000000000000..05efb845c12ed5 --- /dev/null +++ b/homeassistant/components/mill/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mill", + "name": "Mill", + "documentation": "https://www.home-assistant.io/components/mill", + "requirements": [ + "millheater==0.3.4" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/min_max/manifest.json b/homeassistant/components/min_max/manifest.json new file mode 100644 index 00000000000000..ea6befe498b42e --- /dev/null +++ b/homeassistant/components/min_max/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "min_max", + "name": "Min max", + "documentation": "https://www.home-assistant.io/components/min_max", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/mitemp_bt/manifest.json b/homeassistant/components/mitemp_bt/manifest.json new file mode 100644 index 00000000000000..2324a861b38e5d --- /dev/null +++ b/homeassistant/components/mitemp_bt/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mitemp_bt", + "name": "Mitemp bt", + "documentation": "https://www.home-assistant.io/components/mitemp_bt", + "requirements": [ + "mitemp_bt==0.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mjpeg/manifest.json b/homeassistant/components/mjpeg/manifest.json new file mode 100644 index 00000000000000..2ecd66910be6c0 --- /dev/null +++ b/homeassistant/components/mjpeg/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "mjpeg", + "name": "Mjpeg", + "documentation": "https://www.home-assistant.io/components/mjpeg", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mobile_app/manifest.json b/homeassistant/components/mobile_app/manifest.json new file mode 100644 index 00000000000000..9c21858df1d9d2 --- /dev/null +++ b/homeassistant/components/mobile_app/manifest.json @@ -0,0 +1,16 @@ +{ + "domain": "mobile_app", + "name": "Home Assistant Mobile App Support", + "documentation": "https://www.home-assistant.io/components/mobile_app", + "requirements": [ + "PyNaCl==1.3.0" + ], + "dependencies": [ + "device_tracker", + "http", + "webhook" + ], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/mochad/manifest.json b/homeassistant/components/mochad/manifest.json new file mode 100644 index 00000000000000..0e5c4dd1ff3c1b --- /dev/null +++ b/homeassistant/components/mochad/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mochad", + "name": "Mochad", + "documentation": "https://www.home-assistant.io/components/mochad", + "requirements": [ + "pymochad==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/modbus/manifest.json b/homeassistant/components/modbus/manifest.json new file mode 100644 index 00000000000000..e27f594b0af020 --- /dev/null +++ b/homeassistant/components/modbus/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "modbus", + "name": "Modbus", + "documentation": "https://www.home-assistant.io/components/modbus", + "requirements": [ + "pymodbus==1.5.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/modem_callerid/manifest.json b/homeassistant/components/modem_callerid/manifest.json new file mode 100644 index 00000000000000..e3d6d19b803ddf --- /dev/null +++ b/homeassistant/components/modem_callerid/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "modem_callerid", + "name": "Modem callerid", + "documentation": "https://www.home-assistant.io/components/modem_callerid", + "requirements": [ + "basicmodem==0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mold_indicator/manifest.json b/homeassistant/components/mold_indicator/manifest.json new file mode 100644 index 00000000000000..de4680927a4737 --- /dev/null +++ b/homeassistant/components/mold_indicator/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "mold_indicator", + "name": "Mold indicator", + "documentation": "https://www.home-assistant.io/components/mold_indicator", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/monoprice/manifest.json b/homeassistant/components/monoprice/manifest.json new file mode 100644 index 00000000000000..aa07911a697304 --- /dev/null +++ b/homeassistant/components/monoprice/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "monoprice", + "name": "Monoprice", + "documentation": "https://www.home-assistant.io/components/monoprice", + "requirements": [ + "pymonoprice==0.3" + ], + "dependencies": [], + "codeowners": [ + "@etsinko" + ] +} diff --git a/homeassistant/components/moon/manifest.json b/homeassistant/components/moon/manifest.json new file mode 100644 index 00000000000000..50a93fce20a50c --- /dev/null +++ b/homeassistant/components/moon/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "moon", + "name": "Moon", + "documentation": "https://www.home-assistant.io/components/moon", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/mopar/manifest.json b/homeassistant/components/mopar/manifest.json new file mode 100644 index 00000000000000..5acd5bbdcdbbbd --- /dev/null +++ b/homeassistant/components/mopar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mopar", + "name": "Mopar", + "documentation": "https://www.home-assistant.io/components/mopar", + "requirements": [ + "motorparts==1.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mpchc/manifest.json b/homeassistant/components/mpchc/manifest.json new file mode 100644 index 00000000000000..e874ca288912b7 --- /dev/null +++ b/homeassistant/components/mpchc/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "mpchc", + "name": "Mpchc", + "documentation": "https://www.home-assistant.io/components/mpchc", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mpd/manifest.json b/homeassistant/components/mpd/manifest.json new file mode 100644 index 00000000000000..beee3137ef544e --- /dev/null +++ b/homeassistant/components/mpd/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mpd", + "name": "Mpd", + "documentation": "https://www.home-assistant.io/components/mpd", + "requirements": [ + "python-mpd2==1.0.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/mqtt/manifest.json b/homeassistant/components/mqtt/manifest.json new file mode 100644 index 00000000000000..deed878711a100 --- /dev/null +++ b/homeassistant/components/mqtt/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "mqtt", + "name": "MQTT", + "documentation": "https://www.home-assistant.io/components/mqtt", + "requirements": [ + "hbmqtt==0.9.4", + "paho-mqtt==1.4.0" + ], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/mqtt_eventstream/manifest.json b/homeassistant/components/mqtt_eventstream/manifest.json new file mode 100644 index 00000000000000..e795c8aaf181d8 --- /dev/null +++ b/homeassistant/components/mqtt_eventstream/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mqtt_eventstream", + "name": "Mqtt eventstream", + "documentation": "https://www.home-assistant.io/components/mqtt_eventstream", + "requirements": [], + "dependencies": [ + "mqtt" + ], + "codeowners": [] +} diff --git a/homeassistant/components/mqtt_json/manifest.json b/homeassistant/components/mqtt_json/manifest.json new file mode 100644 index 00000000000000..96a0a187e65c65 --- /dev/null +++ b/homeassistant/components/mqtt_json/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "mqtt_json", + "name": "Mqtt json", + "documentation": "https://www.home-assistant.io/components/mqtt_json", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mqtt_room/manifest.json b/homeassistant/components/mqtt_room/manifest.json new file mode 100644 index 00000000000000..e7b37aec50d430 --- /dev/null +++ b/homeassistant/components/mqtt_room/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "mqtt_room", + "name": "Mqtt room", + "documentation": "https://www.home-assistant.io/components/mqtt_room", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mqtt_statestream/manifest.json b/homeassistant/components/mqtt_statestream/manifest.json new file mode 100644 index 00000000000000..5fa9936372932d --- /dev/null +++ b/homeassistant/components/mqtt_statestream/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mqtt_statestream", + "name": "Mqtt statestream", + "documentation": "https://www.home-assistant.io/components/mqtt_statestream", + "requirements": [], + "dependencies": [ + "mqtt" + ], + "codeowners": [] +} diff --git a/homeassistant/components/mvglive/manifest.json b/homeassistant/components/mvglive/manifest.json new file mode 100644 index 00000000000000..5626e2444849fb --- /dev/null +++ b/homeassistant/components/mvglive/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mvglive", + "name": "Mvglive", + "documentation": "https://www.home-assistant.io/components/mvglive", + "requirements": [ + "PyMVGLive==1.1.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mychevy/manifest.json b/homeassistant/components/mychevy/manifest.json new file mode 100644 index 00000000000000..1ff997372ed214 --- /dev/null +++ b/homeassistant/components/mychevy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mychevy", + "name": "Mychevy", + "documentation": "https://www.home-assistant.io/components/mychevy", + "requirements": [ + "mychevy==1.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mycroft/manifest.json b/homeassistant/components/mycroft/manifest.json new file mode 100644 index 00000000000000..77e5a524aacc47 --- /dev/null +++ b/homeassistant/components/mycroft/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mycroft", + "name": "Mycroft", + "documentation": "https://www.home-assistant.io/components/mycroft", + "requirements": [ + "mycroftapi==2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json new file mode 100644 index 00000000000000..3dbabd4260dacf --- /dev/null +++ b/homeassistant/components/myq/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "myq", + "name": "Myq", + "documentation": "https://www.home-assistant.io/components/myq", + "requirements": [ + "pymyq==1.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mysensors/manifest.json b/homeassistant/components/mysensors/manifest.json new file mode 100644 index 00000000000000..2b94c2678aa226 --- /dev/null +++ b/homeassistant/components/mysensors/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mysensors", + "name": "Mysensors", + "documentation": "https://www.home-assistant.io/components/mysensors", + "requirements": [ + "pymysensors==0.18.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/mystrom/manifest.json b/homeassistant/components/mystrom/manifest.json new file mode 100644 index 00000000000000..a3744baccb13a3 --- /dev/null +++ b/homeassistant/components/mystrom/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "mystrom", + "name": "Mystrom", + "documentation": "https://www.home-assistant.io/components/mystrom", + "requirements": [ + "python-mystrom==0.5.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/mythicbeastsdns/manifest.json b/homeassistant/components/mythicbeastsdns/manifest.json new file mode 100644 index 00000000000000..4e37544a99ad2c --- /dev/null +++ b/homeassistant/components/mythicbeastsdns/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "mythicbeastsdns", + "name": "Mythicbeastsdns", + "documentation": "https://www.home-assistant.io/components/mythicbeastsdns", + "requirements": [ + "mbddns==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nad/manifest.json b/homeassistant/components/nad/manifest.json new file mode 100644 index 00000000000000..c624acd73da5e4 --- /dev/null +++ b/homeassistant/components/nad/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nad", + "name": "Nad", + "documentation": "https://www.home-assistant.io/components/nad", + "requirements": [ + "nad_receiver==0.0.11" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/namecheapdns/manifest.json b/homeassistant/components/namecheapdns/manifest.json new file mode 100644 index 00000000000000..c5c46b92166528 --- /dev/null +++ b/homeassistant/components/namecheapdns/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "namecheapdns", + "name": "Namecheapdns", + "documentation": "https://www.home-assistant.io/components/namecheapdns", + "requirements": [ + "defusedxml==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nanoleaf/manifest.json b/homeassistant/components/nanoleaf/manifest.json new file mode 100644 index 00000000000000..a59a6352af213d --- /dev/null +++ b/homeassistant/components/nanoleaf/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nanoleaf", + "name": "Nanoleaf", + "documentation": "https://www.home-assistant.io/components/nanoleaf", + "requirements": [ + "pynanoleaf==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/neato/manifest.json b/homeassistant/components/neato/manifest.json new file mode 100644 index 00000000000000..042d7dcef09dec --- /dev/null +++ b/homeassistant/components/neato/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "neato", + "name": "Neato", + "documentation": "https://www.home-assistant.io/components/neato", + "requirements": [ + "pybotvac==0.0.13" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nederlandse_spoorwegen/manifest.json b/homeassistant/components/nederlandse_spoorwegen/manifest.json new file mode 100644 index 00000000000000..baa6551cc7c2fa --- /dev/null +++ b/homeassistant/components/nederlandse_spoorwegen/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nederlandse_spoorwegen", + "name": "Nederlandse spoorwegen", + "documentation": "https://www.home-assistant.io/components/nederlandse_spoorwegen", + "requirements": [ + "nsapi==2.7.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nello/manifest.json b/homeassistant/components/nello/manifest.json new file mode 100644 index 00000000000000..0caafd7e27adf7 --- /dev/null +++ b/homeassistant/components/nello/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nello", + "name": "Nello", + "documentation": "https://www.home-assistant.io/components/nello", + "requirements": [ + "pynello==2.0.2" + ], + "dependencies": [], + "codeowners": [ + "@pschmitt" + ] +} diff --git a/homeassistant/components/ness_alarm/manifest.json b/homeassistant/components/ness_alarm/manifest.json new file mode 100644 index 00000000000000..93b19470ac434f --- /dev/null +++ b/homeassistant/components/ness_alarm/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ness_alarm", + "name": "Ness alarm", + "documentation": "https://www.home-assistant.io/components/ness_alarm", + "requirements": [ + "nessclient==0.9.15" + ], + "dependencies": [], + "codeowners": [ + "@nickw444" + ] +} diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json new file mode 100644 index 00000000000000..9f2e4202f93253 --- /dev/null +++ b/homeassistant/components/nest/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nest", + "name": "Nest", + "documentation": "https://www.home-assistant.io/components/nest", + "requirements": [ + "python-nest==4.1.0" + ], + "dependencies": [], + "codeowners": [ + "@awarecan" + ] +} diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json new file mode 100644 index 00000000000000..fa6789b81e63dd --- /dev/null +++ b/homeassistant/components/netatmo/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "netatmo", + "name": "Netatmo", + "documentation": "https://www.home-assistant.io/components/netatmo", + "requirements": [ + "pyatmo==1.9" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/netatmo_public/manifest.json b/homeassistant/components/netatmo_public/manifest.json new file mode 100644 index 00000000000000..4327db3f298d6a --- /dev/null +++ b/homeassistant/components/netatmo_public/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "netatmo_public", + "name": "Netatmo public", + "documentation": "https://www.home-assistant.io/components/netatmo_public", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/netdata/manifest.json b/homeassistant/components/netdata/manifest.json new file mode 100644 index 00000000000000..9c3b8ad33d23c9 --- /dev/null +++ b/homeassistant/components/netdata/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "netdata", + "name": "Netdata", + "documentation": "https://www.home-assistant.io/components/netdata", + "requirements": [ + "netdata==0.1.2" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/netgear/manifest.json b/homeassistant/components/netgear/manifest.json new file mode 100644 index 00000000000000..8fbf185c6af51e --- /dev/null +++ b/homeassistant/components/netgear/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "netgear", + "name": "Netgear", + "documentation": "https://www.home-assistant.io/components/netgear", + "requirements": [ + "pynetgear==0.5.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/netgear_lte/manifest.json b/homeassistant/components/netgear_lte/manifest.json new file mode 100644 index 00000000000000..c35895c8c0f401 --- /dev/null +++ b/homeassistant/components/netgear_lte/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "netgear_lte", + "name": "Netgear lte", + "documentation": "https://www.home-assistant.io/components/netgear_lte", + "requirements": [ + "eternalegypt==0.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/netio/manifest.json b/homeassistant/components/netio/manifest.json new file mode 100644 index 00000000000000..75649c66abb586 --- /dev/null +++ b/homeassistant/components/netio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "netio", + "name": "Netio", + "documentation": "https://www.home-assistant.io/components/netio", + "requirements": [ + "pynetio==0.1.9.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/neurio_energy/manifest.json b/homeassistant/components/neurio_energy/manifest.json new file mode 100644 index 00000000000000..04420d5c4f2116 --- /dev/null +++ b/homeassistant/components/neurio_energy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "neurio_energy", + "name": "Neurio energy", + "documentation": "https://www.home-assistant.io/components/neurio_energy", + "requirements": [ + "neurio==0.3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nfandroidtv/manifest.json b/homeassistant/components/nfandroidtv/manifest.json new file mode 100644 index 00000000000000..8f3d88b58ee604 --- /dev/null +++ b/homeassistant/components/nfandroidtv/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "nfandroidtv", + "name": "Nfandroidtv", + "documentation": "https://www.home-assistant.io/components/nfandroidtv", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/niko_home_control/manifest.json b/homeassistant/components/niko_home_control/manifest.json new file mode 100644 index 00000000000000..6f5ce87d8e1be7 --- /dev/null +++ b/homeassistant/components/niko_home_control/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "niko_home_control", + "name": "Niko home control", + "documentation": "https://www.home-assistant.io/components/niko_home_control", + "requirements": [ + "niko-home-control==0.1.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nilu/manifest.json b/homeassistant/components/nilu/manifest.json new file mode 100644 index 00000000000000..ee7645653e6d0b --- /dev/null +++ b/homeassistant/components/nilu/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nilu", + "name": "Nilu", + "documentation": "https://www.home-assistant.io/components/nilu", + "requirements": [ + "niluclient==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nissan_leaf/manifest.json b/homeassistant/components/nissan_leaf/manifest.json new file mode 100644 index 00000000000000..ab94c01b7c1272 --- /dev/null +++ b/homeassistant/components/nissan_leaf/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nissan_leaf", + "name": "Nissan leaf", + "documentation": "https://www.home-assistant.io/components/nissan_leaf", + "requirements": [ + "pycarwings2==2.8" + ], + "dependencies": [], + "codeowners": [ + "@filcole" + ] +} diff --git a/homeassistant/components/nmap_tracker/manifest.json b/homeassistant/components/nmap_tracker/manifest.json new file mode 100644 index 00000000000000..f4c4d33f0365db --- /dev/null +++ b/homeassistant/components/nmap_tracker/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nmap_tracker", + "name": "Nmap tracker", + "documentation": "https://www.home-assistant.io/components/nmap_tracker", + "requirements": [ + "python-nmap==0.6.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nmbs/manifest.json b/homeassistant/components/nmbs/manifest.json new file mode 100644 index 00000000000000..1a2fa0556883f1 --- /dev/null +++ b/homeassistant/components/nmbs/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nmbs", + "name": "Nmbs", + "documentation": "https://www.home-assistant.io/components/nmbs", + "requirements": [ + "pyrail==0.0.3" + ], + "dependencies": [], + "codeowners": [ + "@thibmaek" + ] +} diff --git a/homeassistant/components/no_ip/manifest.json b/homeassistant/components/no_ip/manifest.json new file mode 100644 index 00000000000000..125815995329e4 --- /dev/null +++ b/homeassistant/components/no_ip/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "no_ip", + "name": "No ip", + "documentation": "https://www.home-assistant.io/components/no_ip", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/noaa_tides/manifest.json b/homeassistant/components/noaa_tides/manifest.json new file mode 100644 index 00000000000000..9ffc0215fd15b7 --- /dev/null +++ b/homeassistant/components/noaa_tides/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "noaa_tides", + "name": "Noaa tides", + "documentation": "https://www.home-assistant.io/components/noaa_tides", + "requirements": [ + "py_noaa==0.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/norway_air/manifest.json b/homeassistant/components/norway_air/manifest.json new file mode 100644 index 00000000000000..08c9932c36f5ce --- /dev/null +++ b/homeassistant/components/norway_air/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "norway_air", + "name": "Norway air", + "documentation": "https://www.home-assistant.io/components/norway_air", + "requirements": [ + "pyMetno==0.4.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/notify/manifest.json b/homeassistant/components/notify/manifest.json new file mode 100644 index 00000000000000..22c85723cb87e2 --- /dev/null +++ b/homeassistant/components/notify/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "notify", + "name": "Notify", + "documentation": "https://www.home-assistant.io/components/notify", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@flowolf" + ] +} diff --git a/homeassistant/components/nsw_fuel_station/manifest.json b/homeassistant/components/nsw_fuel_station/manifest.json new file mode 100644 index 00000000000000..6be24fb5a2cb0c --- /dev/null +++ b/homeassistant/components/nsw_fuel_station/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nsw_fuel_station", + "name": "Nsw fuel station", + "documentation": "https://www.home-assistant.io/components/nsw_fuel_station", + "requirements": [ + "nsw-fuel-api-client==1.0.10" + ], + "dependencies": [], + "codeowners": [ + "@nickw444" + ] +} diff --git a/homeassistant/components/nsw_rural_fire_service_feed/manifest.json b/homeassistant/components/nsw_rural_fire_service_feed/manifest.json new file mode 100644 index 00000000000000..dd0ba048a3497b --- /dev/null +++ b/homeassistant/components/nsw_rural_fire_service_feed/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nsw_rural_fire_service_feed", + "name": "Nsw rural fire service feed", + "documentation": "https://www.home-assistant.io/components/nsw_rural_fire_service_feed", + "requirements": [ + "geojson_client==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nuheat/manifest.json b/homeassistant/components/nuheat/manifest.json new file mode 100644 index 00000000000000..c9e69c44ec2e19 --- /dev/null +++ b/homeassistant/components/nuheat/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nuheat", + "name": "Nuheat", + "documentation": "https://www.home-assistant.io/components/nuheat", + "requirements": [ + "nuheat==0.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nuimo_controller/manifest.json b/homeassistant/components/nuimo_controller/manifest.json new file mode 100644 index 00000000000000..9f18d2849f8ee3 --- /dev/null +++ b/homeassistant/components/nuimo_controller/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nuimo_controller", + "name": "Nuimo controller", + "documentation": "https://www.home-assistant.io/components/nuimo_controller", + "requirements": [ + "--only-binary=all nuimo==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nuki/manifest.json b/homeassistant/components/nuki/manifest.json new file mode 100644 index 00000000000000..d031cf6ce5ff7b --- /dev/null +++ b/homeassistant/components/nuki/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "nuki", + "name": "Nuki", + "documentation": "https://www.home-assistant.io/components/nuki", + "requirements": [ + "pynuki==1.3.2" + ], + "dependencies": [], + "codeowners": [ + "@pschmitt" + ] +} diff --git a/homeassistant/components/nut/manifest.json b/homeassistant/components/nut/manifest.json new file mode 100644 index 00000000000000..920e56fba7cd47 --- /dev/null +++ b/homeassistant/components/nut/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nut", + "name": "Nut", + "documentation": "https://www.home-assistant.io/components/nut", + "requirements": [ + "pynut2==2.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nx584/manifest.json b/homeassistant/components/nx584/manifest.json new file mode 100644 index 00000000000000..67b5b0e2eeb96c --- /dev/null +++ b/homeassistant/components/nx584/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "nx584", + "name": "Nx584", + "documentation": "https://www.home-assistant.io/components/nx584", + "requirements": [ + "pynx584==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/nzbget/manifest.json b/homeassistant/components/nzbget/manifest.json new file mode 100644 index 00000000000000..69293ede516aee --- /dev/null +++ b/homeassistant/components/nzbget/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "nzbget", + "name": "Nzbget", + "documentation": "https://www.home-assistant.io/components/nzbget", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/octoprint/manifest.json b/homeassistant/components/octoprint/manifest.json new file mode 100644 index 00000000000000..c34e1458e4bdb5 --- /dev/null +++ b/homeassistant/components/octoprint/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "octoprint", + "name": "Octoprint", + "documentation": "https://www.home-assistant.io/components/octoprint", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/oem/manifest.json b/homeassistant/components/oem/manifest.json new file mode 100644 index 00000000000000..d23b07b2756633 --- /dev/null +++ b/homeassistant/components/oem/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "oem", + "name": "Oem", + "documentation": "https://www.home-assistant.io/components/oem", + "requirements": [ + "oemthermostat==1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ohmconnect/manifest.json b/homeassistant/components/ohmconnect/manifest.json new file mode 100644 index 00000000000000..a163a7d673af17 --- /dev/null +++ b/homeassistant/components/ohmconnect/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ohmconnect", + "name": "Ohmconnect", + "documentation": "https://www.home-assistant.io/components/ohmconnect", + "requirements": [ + "defusedxml==0.5.0" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/onboarding/manifest.json b/homeassistant/components/onboarding/manifest.json new file mode 100644 index 00000000000000..ffb01bd56021d5 --- /dev/null +++ b/homeassistant/components/onboarding/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "onboarding", + "name": "Onboarding", + "documentation": "https://www.home-assistant.io/components/onboarding", + "requirements": [], + "dependencies": [ + "auth", + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/onewire/manifest.json b/homeassistant/components/onewire/manifest.json new file mode 100644 index 00000000000000..00075d4485f4e4 --- /dev/null +++ b/homeassistant/components/onewire/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "onewire", + "name": "Onewire", + "documentation": "https://www.home-assistant.io/components/onewire", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/onkyo/manifest.json b/homeassistant/components/onkyo/manifest.json new file mode 100644 index 00000000000000..7fd27dd7edf8b1 --- /dev/null +++ b/homeassistant/components/onkyo/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "onkyo", + "name": "Onkyo", + "documentation": "https://www.home-assistant.io/components/onkyo", + "requirements": [ + "onkyo-eiscp==1.2.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/onvif/manifest.json b/homeassistant/components/onvif/manifest.json new file mode 100644 index 00000000000000..6d5ad256f165cb --- /dev/null +++ b/homeassistant/components/onvif/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "onvif", + "name": "Onvif", + "documentation": "https://www.home-assistant.io/components/onvif", + "requirements": [ + "onvif-py3==0.1.3", + "suds-passworddigest-homeassistant==0.1.2a0.dev0", + "suds-py3==1.3.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openalpr_cloud/manifest.json b/homeassistant/components/openalpr_cloud/manifest.json new file mode 100644 index 00000000000000..f0421295836f03 --- /dev/null +++ b/homeassistant/components/openalpr_cloud/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "openalpr_cloud", + "name": "Openalpr cloud", + "documentation": "https://www.home-assistant.io/components/openalpr_cloud", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openalpr_local/manifest.json b/homeassistant/components/openalpr_local/manifest.json new file mode 100644 index 00000000000000..3c92e840f43406 --- /dev/null +++ b/homeassistant/components/openalpr_local/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "openalpr_local", + "name": "Openalpr local", + "documentation": "https://www.home-assistant.io/components/openalpr_local", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/opencv/manifest.json b/homeassistant/components/opencv/manifest.json new file mode 100644 index 00000000000000..b49e5b735549ab --- /dev/null +++ b/homeassistant/components/opencv/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "opencv", + "name": "Opencv", + "documentation": "https://www.home-assistant.io/components/opencv", + "requirements": [ + "numpy==1.16.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openevse/manifest.json b/homeassistant/components/openevse/manifest.json new file mode 100644 index 00000000000000..f37c769d20e5be --- /dev/null +++ b/homeassistant/components/openevse/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "openevse", + "name": "Openevse", + "documentation": "https://www.home-assistant.io/components/openevse", + "requirements": [ + "openevsewifi==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openexchangerates/manifest.json b/homeassistant/components/openexchangerates/manifest.json new file mode 100644 index 00000000000000..ffb86d4a5e2d6c --- /dev/null +++ b/homeassistant/components/openexchangerates/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "openexchangerates", + "name": "Openexchangerates", + "documentation": "https://www.home-assistant.io/components/openexchangerates", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/opengarage/manifest.json b/homeassistant/components/opengarage/manifest.json new file mode 100644 index 00000000000000..95f944b7087a75 --- /dev/null +++ b/homeassistant/components/opengarage/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "opengarage", + "name": "Opengarage", + "documentation": "https://www.home-assistant.io/components/opengarage", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openhardwaremonitor/manifest.json b/homeassistant/components/openhardwaremonitor/manifest.json new file mode 100644 index 00000000000000..d9281f08eda102 --- /dev/null +++ b/homeassistant/components/openhardwaremonitor/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "openhardwaremonitor", + "name": "Openhardwaremonitor", + "documentation": "https://www.home-assistant.io/components/openhardwaremonitor", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openhome/manifest.json b/homeassistant/components/openhome/manifest.json new file mode 100644 index 00000000000000..276346ae79bff0 --- /dev/null +++ b/homeassistant/components/openhome/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "openhome", + "name": "Openhome", + "documentation": "https://www.home-assistant.io/components/openhome", + "requirements": [ + "openhomedevice==0.4.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/opensensemap/manifest.json b/homeassistant/components/opensensemap/manifest.json new file mode 100644 index 00000000000000..ab03f1cf7c6824 --- /dev/null +++ b/homeassistant/components/opensensemap/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "opensensemap", + "name": "Opensensemap", + "documentation": "https://www.home-assistant.io/components/opensensemap", + "requirements": [ + "opensensemap-api==0.1.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/opensky/manifest.json b/homeassistant/components/opensky/manifest.json new file mode 100644 index 00000000000000..dd58cdd416816b --- /dev/null +++ b/homeassistant/components/opensky/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "opensky", + "name": "Opensky", + "documentation": "https://www.home-assistant.io/components/opensky", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/opentherm_gw/manifest.json b/homeassistant/components/opentherm_gw/manifest.json new file mode 100644 index 00000000000000..50bfa4d1122deb --- /dev/null +++ b/homeassistant/components/opentherm_gw/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "opentherm_gw", + "name": "Opentherm gw", + "documentation": "https://www.home-assistant.io/components/opentherm_gw", + "requirements": [ + "pyotgw==0.4b3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/openuv/manifest.json b/homeassistant/components/openuv/manifest.json new file mode 100644 index 00000000000000..b94a409aa7135d --- /dev/null +++ b/homeassistant/components/openuv/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "openuv", + "name": "Openuv", + "documentation": "https://www.home-assistant.io/components/openuv", + "requirements": [ + "pyopenuv==1.0.9" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/openweathermap/manifest.json b/homeassistant/components/openweathermap/manifest.json new file mode 100644 index 00000000000000..d24b23f64bb470 --- /dev/null +++ b/homeassistant/components/openweathermap/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "openweathermap", + "name": "Openweathermap", + "documentation": "https://www.home-assistant.io/components/openweathermap", + "requirements": [ + "pyowm==2.10.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/opple/manifest.json b/homeassistant/components/opple/manifest.json new file mode 100644 index 00000000000000..c10be48f3fa3cc --- /dev/null +++ b/homeassistant/components/opple/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "opple", + "name": "Opple", + "documentation": "https://www.home-assistant.io/components/opple", + "requirements": [ + "pyoppleio==1.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/orvibo/manifest.json b/homeassistant/components/orvibo/manifest.json new file mode 100644 index 00000000000000..73f4eaed7dae11 --- /dev/null +++ b/homeassistant/components/orvibo/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "orvibo", + "name": "Orvibo", + "documentation": "https://www.home-assistant.io/components/orvibo", + "requirements": [ + "orvibo==1.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/osramlightify/manifest.json b/homeassistant/components/osramlightify/manifest.json new file mode 100644 index 00000000000000..0b158b967423b9 --- /dev/null +++ b/homeassistant/components/osramlightify/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "osramlightify", + "name": "Osramlightify", + "documentation": "https://www.home-assistant.io/components/osramlightify", + "requirements": [ + "lightify==1.0.7.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/otp/manifest.json b/homeassistant/components/otp/manifest.json new file mode 100644 index 00000000000000..3eb24e0f1c608e --- /dev/null +++ b/homeassistant/components/otp/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "otp", + "name": "Otp", + "documentation": "https://www.home-assistant.io/components/otp", + "requirements": [ + "pyotp==2.2.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/owlet/manifest.json b/homeassistant/components/owlet/manifest.json new file mode 100644 index 00000000000000..edc51dcc5333ab --- /dev/null +++ b/homeassistant/components/owlet/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "owlet", + "name": "Owlet", + "documentation": "https://www.home-assistant.io/components/owlet", + "requirements": [ + "pyowlet==1.0.2" + ], + "dependencies": [], + "codeowners": [ + "@oblogic7" + ] +} diff --git a/homeassistant/components/owntracks/manifest.json b/homeassistant/components/owntracks/manifest.json new file mode 100644 index 00000000000000..3646f32093a670 --- /dev/null +++ b/homeassistant/components/owntracks/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "owntracks", + "name": "Owntracks", + "documentation": "https://www.home-assistant.io/components/owntracks", + "requirements": [ + "PyNaCl==1.3.0" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/panasonic_bluray/manifest.json b/homeassistant/components/panasonic_bluray/manifest.json new file mode 100644 index 00000000000000..fe2387744ab216 --- /dev/null +++ b/homeassistant/components/panasonic_bluray/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "panasonic_bluray", + "name": "Panasonic bluray", + "documentation": "https://www.home-assistant.io/components/panasonic_bluray", + "requirements": [ + "panacotta==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/panasonic_viera/manifest.json b/homeassistant/components/panasonic_viera/manifest.json new file mode 100644 index 00000000000000..432e729ef20a0c --- /dev/null +++ b/homeassistant/components/panasonic_viera/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "panasonic_viera", + "name": "Panasonic viera", + "documentation": "https://www.home-assistant.io/components/panasonic_viera", + "requirements": [ + "panasonic_viera==0.3.2", + "wakeonlan==1.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pandora/manifest.json b/homeassistant/components/pandora/manifest.json new file mode 100644 index 00000000000000..68e8337a33db05 --- /dev/null +++ b/homeassistant/components/pandora/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pandora", + "name": "Pandora", + "documentation": "https://www.home-assistant.io/components/pandora", + "requirements": [ + "pexpect==4.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/panel_custom/manifest.json b/homeassistant/components/panel_custom/manifest.json new file mode 100644 index 00000000000000..5fb7adb2a4aca6 --- /dev/null +++ b/homeassistant/components/panel_custom/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "panel_custom", + "name": "Panel custom", + "documentation": "https://www.home-assistant.io/components/panel_custom", + "requirements": [], + "dependencies": [ + "frontend" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/panel_iframe/manifest.json b/homeassistant/components/panel_iframe/manifest.json new file mode 100644 index 00000000000000..127ff3caa4d64a --- /dev/null +++ b/homeassistant/components/panel_iframe/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "panel_iframe", + "name": "Panel iframe", + "documentation": "https://www.home-assistant.io/components/panel_iframe", + "requirements": [], + "dependencies": [ + "frontend" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/pencom/manifest.json b/homeassistant/components/pencom/manifest.json new file mode 100644 index 00000000000000..186e071d25b55d --- /dev/null +++ b/homeassistant/components/pencom/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pencom", + "name": "Pencom", + "documentation": "https://www.home-assistant.io/components/pencom", + "requirements": [ + "pencompy==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/persistent_notification/manifest.json b/homeassistant/components/persistent_notification/manifest.json new file mode 100644 index 00000000000000..8bc343e1f0876a --- /dev/null +++ b/homeassistant/components/persistent_notification/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "persistent_notification", + "name": "Persistent notification", + "documentation": "https://www.home-assistant.io/components/persistent_notification", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/person/manifest.json b/homeassistant/components/person/manifest.json new file mode 100644 index 00000000000000..d2cba929259248 --- /dev/null +++ b/homeassistant/components/person/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "person", + "name": "Person", + "documentation": "https://www.home-assistant.io/components/person", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/philips_js/manifest.json b/homeassistant/components/philips_js/manifest.json new file mode 100644 index 00000000000000..18ddcf1f5ffd84 --- /dev/null +++ b/homeassistant/components/philips_js/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "philips_js", + "name": "Philips js", + "documentation": "https://www.home-assistant.io/components/philips_js", + "requirements": [ + "ha-philipsjs==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pi_hole/manifest.json b/homeassistant/components/pi_hole/manifest.json new file mode 100644 index 00000000000000..c47d8811e689eb --- /dev/null +++ b/homeassistant/components/pi_hole/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "pi_hole", + "name": "Pi hole", + "documentation": "https://www.home-assistant.io/components/pi_hole", + "requirements": [ + "hole==0.3.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/picotts/manifest.json b/homeassistant/components/picotts/manifest.json new file mode 100644 index 00000000000000..bfe7f449ca073b --- /dev/null +++ b/homeassistant/components/picotts/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "picotts", + "name": "Picotts", + "documentation": "https://www.home-assistant.io/components/picotts", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/piglow/manifest.json b/homeassistant/components/piglow/manifest.json new file mode 100644 index 00000000000000..67b1033c51ea42 --- /dev/null +++ b/homeassistant/components/piglow/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "piglow", + "name": "Piglow", + "documentation": "https://www.home-assistant.io/components/piglow", + "requirements": [ + "piglow==1.2.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pilight/manifest.json b/homeassistant/components/pilight/manifest.json new file mode 100644 index 00000000000000..dfe4952e1a17fb --- /dev/null +++ b/homeassistant/components/pilight/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pilight", + "name": "Pilight", + "documentation": "https://www.home-assistant.io/components/pilight", + "requirements": [ + "pilight==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ping/manifest.json b/homeassistant/components/ping/manifest.json new file mode 100644 index 00000000000000..d98adef87a7bd4 --- /dev/null +++ b/homeassistant/components/ping/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ping", + "name": "Ping", + "documentation": "https://www.home-assistant.io/components/ping", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pioneer/manifest.json b/homeassistant/components/pioneer/manifest.json new file mode 100644 index 00000000000000..b06874149ed17e --- /dev/null +++ b/homeassistant/components/pioneer/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "pioneer", + "name": "Pioneer", + "documentation": "https://www.home-assistant.io/components/pioneer", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pjlink/manifest.json b/homeassistant/components/pjlink/manifest.json new file mode 100644 index 00000000000000..6901847bd8d0bd --- /dev/null +++ b/homeassistant/components/pjlink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pjlink", + "name": "Pjlink", + "documentation": "https://www.home-assistant.io/components/pjlink", + "requirements": [ + "pypjlink2==1.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/plant/manifest.json b/homeassistant/components/plant/manifest.json new file mode 100644 index 00000000000000..cbde894173b7cf --- /dev/null +++ b/homeassistant/components/plant/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "plant", + "name": "Plant", + "documentation": "https://www.home-assistant.io/components/plant", + "requirements": [], + "dependencies": [ + "group", + "zone" + ], + "codeowners": [ + "@ChristianKuehnel" + ] +} diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json new file mode 100644 index 00000000000000..ae8e1b684ed139 --- /dev/null +++ b/homeassistant/components/plex/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "plex", + "name": "Plex", + "documentation": "https://www.home-assistant.io/components/plex", + "requirements": [ + "plexapi==3.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/plum_lightpad/manifest.json b/homeassistant/components/plum_lightpad/manifest.json new file mode 100644 index 00000000000000..389eca09c4238d --- /dev/null +++ b/homeassistant/components/plum_lightpad/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "plum_lightpad", + "name": "Plum lightpad", + "documentation": "https://www.home-assistant.io/components/plum_lightpad", + "requirements": [ + "plumlightpad==0.0.11" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pocketcasts/manifest.json b/homeassistant/components/pocketcasts/manifest.json new file mode 100644 index 00000000000000..11c202363246a7 --- /dev/null +++ b/homeassistant/components/pocketcasts/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pocketcasts", + "name": "Pocketcasts", + "documentation": "https://www.home-assistant.io/components/pocketcasts", + "requirements": [ + "pocketcasts==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/point/manifest.json b/homeassistant/components/point/manifest.json new file mode 100644 index 00000000000000..8b888a3647ad2e --- /dev/null +++ b/homeassistant/components/point/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "point", + "name": "Point", + "documentation": "https://www.home-assistant.io/components/point", + "requirements": [ + "pypoint==1.1.1" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [ + "@fredrike" + ] +} diff --git a/homeassistant/components/pollen/manifest.json b/homeassistant/components/pollen/manifest.json new file mode 100644 index 00000000000000..2edf83a0d1f3f5 --- /dev/null +++ b/homeassistant/components/pollen/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "pollen", + "name": "Pollen", + "documentation": "https://www.home-assistant.io/components/pollen", + "requirements": [ + "numpy==1.16.2", + "pypollencom==2.2.3" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/postnl/manifest.json b/homeassistant/components/postnl/manifest.json new file mode 100644 index 00000000000000..9746cb168aa137 --- /dev/null +++ b/homeassistant/components/postnl/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "postnl", + "name": "Postnl", + "documentation": "https://www.home-assistant.io/components/postnl", + "requirements": [ + "postnl_api==1.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/prezzibenzina/manifest.json b/homeassistant/components/prezzibenzina/manifest.json new file mode 100644 index 00000000000000..2427ebbfdb0508 --- /dev/null +++ b/homeassistant/components/prezzibenzina/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "prezzibenzina", + "name": "Prezzibenzina", + "documentation": "https://www.home-assistant.io/components/prezzibenzina", + "requirements": [ + "prezzibenzina-py==1.1.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/proliphix/manifest.json b/homeassistant/components/proliphix/manifest.json new file mode 100644 index 00000000000000..3aa356823c1823 --- /dev/null +++ b/homeassistant/components/proliphix/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "proliphix", + "name": "Proliphix", + "documentation": "https://www.home-assistant.io/components/proliphix", + "requirements": [ + "proliphix==0.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/prometheus/manifest.json b/homeassistant/components/prometheus/manifest.json new file mode 100644 index 00000000000000..d9699be6bf7109 --- /dev/null +++ b/homeassistant/components/prometheus/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "prometheus", + "name": "Prometheus", + "documentation": "https://www.home-assistant.io/components/prometheus", + "requirements": [ + "prometheus_client==0.2.0" + ], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/prowl/manifest.json b/homeassistant/components/prowl/manifest.json new file mode 100644 index 00000000000000..a8b4893c995a29 --- /dev/null +++ b/homeassistant/components/prowl/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "prowl", + "name": "Prowl", + "documentation": "https://www.home-assistant.io/components/prowl", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/proximity/manifest.json b/homeassistant/components/proximity/manifest.json new file mode 100644 index 00000000000000..335bea82fc91d8 --- /dev/null +++ b/homeassistant/components/proximity/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "proximity", + "name": "Proximity", + "documentation": "https://www.home-assistant.io/components/proximity", + "requirements": [], + "dependencies": [ + "device_tracker", + "zone" + ], + "codeowners": [] +} diff --git a/homeassistant/components/proxy/manifest.json b/homeassistant/components/proxy/manifest.json new file mode 100644 index 00000000000000..a4a33efa2cdceb --- /dev/null +++ b/homeassistant/components/proxy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "proxy", + "name": "Proxy", + "documentation": "https://www.home-assistant.io/components/proxy", + "requirements": [ + "pillow==5.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ps4/manifest.json b/homeassistant/components/ps4/manifest.json new file mode 100644 index 00000000000000..605dd3f530ce23 --- /dev/null +++ b/homeassistant/components/ps4/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ps4", + "name": "Ps4", + "documentation": "https://www.home-assistant.io/components/ps4", + "requirements": [ + "pyps4-homeassistant==0.5.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pulseaudio_loopback/manifest.json b/homeassistant/components/pulseaudio_loopback/manifest.json new file mode 100644 index 00000000000000..58a2871e027939 --- /dev/null +++ b/homeassistant/components/pulseaudio_loopback/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "pulseaudio_loopback", + "name": "Pulseaudio loopback", + "documentation": "https://www.home-assistant.io/components/pulseaudio_loopback", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/push/manifest.json b/homeassistant/components/push/manifest.json new file mode 100644 index 00000000000000..96b9e647e14fee --- /dev/null +++ b/homeassistant/components/push/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "push", + "name": "Push", + "documentation": "https://www.home-assistant.io/components/push", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/pushbullet/manifest.json b/homeassistant/components/pushbullet/manifest.json new file mode 100644 index 00000000000000..51e77959d7ad9c --- /dev/null +++ b/homeassistant/components/pushbullet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pushbullet", + "name": "Pushbullet", + "documentation": "https://www.home-assistant.io/components/pushbullet", + "requirements": [ + "pushbullet.py==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pushetta/manifest.json b/homeassistant/components/pushetta/manifest.json new file mode 100644 index 00000000000000..b42180c7268035 --- /dev/null +++ b/homeassistant/components/pushetta/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pushetta", + "name": "Pushetta", + "documentation": "https://www.home-assistant.io/components/pushetta", + "requirements": [ + "pushetta==1.0.15" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pushover/manifest.json b/homeassistant/components/pushover/manifest.json new file mode 100644 index 00000000000000..30dd35720de2a4 --- /dev/null +++ b/homeassistant/components/pushover/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pushover", + "name": "Pushover", + "documentation": "https://www.home-assistant.io/components/pushover", + "requirements": [ + "python-pushover==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pushsafer/manifest.json b/homeassistant/components/pushsafer/manifest.json new file mode 100644 index 00000000000000..300d0ead4a5c3a --- /dev/null +++ b/homeassistant/components/pushsafer/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "pushsafer", + "name": "Pushsafer", + "documentation": "https://www.home-assistant.io/components/pushsafer", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/pvoutput/manifest.json b/homeassistant/components/pvoutput/manifest.json new file mode 100644 index 00000000000000..b61c7100828f09 --- /dev/null +++ b/homeassistant/components/pvoutput/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "pvoutput", + "name": "Pvoutput", + "documentation": "https://www.home-assistant.io/components/pvoutput", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/pyload/manifest.json b/homeassistant/components/pyload/manifest.json new file mode 100644 index 00000000000000..437bd3bc4d2ce1 --- /dev/null +++ b/homeassistant/components/pyload/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "pyload", + "name": "Pyload", + "documentation": "https://www.home-assistant.io/components/pyload", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/python_script/manifest.json b/homeassistant/components/python_script/manifest.json new file mode 100644 index 00000000000000..0f88513bb45931 --- /dev/null +++ b/homeassistant/components/python_script/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "python_script", + "name": "Python script", + "documentation": "https://www.home-assistant.io/components/python_script", + "requirements": [ + "restrictedpython==4.0b8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/qbittorrent/manifest.json b/homeassistant/components/qbittorrent/manifest.json new file mode 100644 index 00000000000000..5fb850739d8d0d --- /dev/null +++ b/homeassistant/components/qbittorrent/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "qbittorrent", + "name": "Qbittorrent", + "documentation": "https://www.home-assistant.io/components/qbittorrent", + "requirements": [ + "python-qbittorrent==0.3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/qnap/manifest.json b/homeassistant/components/qnap/manifest.json new file mode 100644 index 00000000000000..f02d416c7e6be9 --- /dev/null +++ b/homeassistant/components/qnap/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "qnap", + "name": "Qnap", + "documentation": "https://www.home-assistant.io/components/qnap", + "requirements": [ + "qnapstats==0.2.7" + ], + "dependencies": [], + "codeowners": [ + "@colinodell" + ] +} diff --git a/homeassistant/components/qrcode/manifest.json b/homeassistant/components/qrcode/manifest.json new file mode 100644 index 00000000000000..96a351ac453413 --- /dev/null +++ b/homeassistant/components/qrcode/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "qrcode", + "name": "Qrcode", + "documentation": "https://www.home-assistant.io/components/qrcode", + "requirements": [ + "pillow==5.4.1", + "pyzbar==0.1.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/quantum_gateway/manifest.json b/homeassistant/components/quantum_gateway/manifest.json new file mode 100644 index 00000000000000..9c062482a4c2f2 --- /dev/null +++ b/homeassistant/components/quantum_gateway/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "quantum_gateway", + "name": "Quantum gateway", + "documentation": "https://www.home-assistant.io/components/quantum_gateway", + "requirements": [ + "quantum-gateway==0.0.5" + ], + "dependencies": [], + "codeowners": [ + "@cisasteelersfan" + ] +} diff --git a/homeassistant/components/qwikswitch/manifest.json b/homeassistant/components/qwikswitch/manifest.json new file mode 100644 index 00000000000000..4907cb462b6f1a --- /dev/null +++ b/homeassistant/components/qwikswitch/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "qwikswitch", + "name": "Qwikswitch", + "documentation": "https://www.home-assistant.io/components/qwikswitch", + "requirements": [ + "pyqwikswitch==0.93" + ], + "dependencies": [], + "codeowners": [ + "@kellerza" + ] +} diff --git a/homeassistant/components/rachio/manifest.json b/homeassistant/components/rachio/manifest.json new file mode 100644 index 00000000000000..30bde9a297d396 --- /dev/null +++ b/homeassistant/components/rachio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rachio", + "name": "Rachio", + "documentation": "https://www.home-assistant.io/components/rachio", + "requirements": [ + "rachiopy==0.1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/radarr/manifest.json b/homeassistant/components/radarr/manifest.json new file mode 100644 index 00000000000000..f12fcf4220cfe9 --- /dev/null +++ b/homeassistant/components/radarr/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "radarr", + "name": "Radarr", + "documentation": "https://www.home-assistant.io/components/radarr", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/radiotherm/manifest.json b/homeassistant/components/radiotherm/manifest.json new file mode 100644 index 00000000000000..002fdb632739c0 --- /dev/null +++ b/homeassistant/components/radiotherm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "radiotherm", + "name": "Radiotherm", + "documentation": "https://www.home-assistant.io/components/radiotherm", + "requirements": [ + "radiotherm==2.0.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rainbird/manifest.json b/homeassistant/components/rainbird/manifest.json new file mode 100644 index 00000000000000..24113d6253427f --- /dev/null +++ b/homeassistant/components/rainbird/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rainbird", + "name": "Rainbird", + "documentation": "https://www.home-assistant.io/components/rainbird", + "requirements": [ + "pyrainbird==0.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/raincloud/manifest.json b/homeassistant/components/raincloud/manifest.json new file mode 100644 index 00000000000000..2befec24b91a3d --- /dev/null +++ b/homeassistant/components/raincloud/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "raincloud", + "name": "Raincloud", + "documentation": "https://www.home-assistant.io/components/raincloud", + "requirements": [ + "raincloudy==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json new file mode 100644 index 00000000000000..ad7bdada321624 --- /dev/null +++ b/homeassistant/components/rainmachine/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "rainmachine", + "name": "Rainmachine", + "documentation": "https://www.home-assistant.io/components/rainmachine", + "requirements": [ + "regenmaschine==1.4.0" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/random/manifest.json b/homeassistant/components/random/manifest.json new file mode 100644 index 00000000000000..c184f35734c530 --- /dev/null +++ b/homeassistant/components/random/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "random", + "name": "Random", + "documentation": "https://www.home-assistant.io/components/random", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/raspihats/manifest.json b/homeassistant/components/raspihats/manifest.json new file mode 100644 index 00000000000000..8f5040152a2caf --- /dev/null +++ b/homeassistant/components/raspihats/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "raspihats", + "name": "Raspihats", + "documentation": "https://www.home-assistant.io/components/raspihats", + "requirements": [ + "raspihats==2.2.3", + "smbus-cffi==0.5.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/raspyrfm/manifest.json b/homeassistant/components/raspyrfm/manifest.json new file mode 100644 index 00000000000000..fee815a7e6b17d --- /dev/null +++ b/homeassistant/components/raspyrfm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "raspyrfm", + "name": "Raspyrfm", + "documentation": "https://www.home-assistant.io/components/raspyrfm", + "requirements": [ + "raspyrfm-client==1.2.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/recollect_waste/manifest.json b/homeassistant/components/recollect_waste/manifest.json new file mode 100644 index 00000000000000..2cccf32f298608 --- /dev/null +++ b/homeassistant/components/recollect_waste/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "recollect_waste", + "name": "Recollect waste", + "documentation": "https://www.home-assistant.io/components/recollect_waste", + "requirements": [ + "recollect-waste==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/recorder/manifest.json b/homeassistant/components/recorder/manifest.json new file mode 100644 index 00000000000000..c466d35e23fd82 --- /dev/null +++ b/homeassistant/components/recorder/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "recorder", + "name": "Recorder", + "documentation": "https://www.home-assistant.io/components/recorder", + "requirements": [ + "sqlalchemy==1.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/recswitch/manifest.json b/homeassistant/components/recswitch/manifest.json new file mode 100644 index 00000000000000..af8e802c5ec2be --- /dev/null +++ b/homeassistant/components/recswitch/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "recswitch", + "name": "Recswitch", + "documentation": "https://www.home-assistant.io/components/recswitch", + "requirements": [ + "pyrecswitch==1.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/reddit/manifest.json b/homeassistant/components/reddit/manifest.json new file mode 100644 index 00000000000000..72ee7a42ca4130 --- /dev/null +++ b/homeassistant/components/reddit/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "reddit", + "name": "Reddit", + "documentation": "https://www.home-assistant.io/components/reddit", + "requirements": [ + "praw==6.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rejseplanen/manifest.json b/homeassistant/components/rejseplanen/manifest.json new file mode 100644 index 00000000000000..7256239933006f --- /dev/null +++ b/homeassistant/components/rejseplanen/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rejseplanen", + "name": "Rejseplanen", + "documentation": "https://www.home-assistant.io/components/rejseplanen", + "requirements": [ + "rjpl==0.3.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/remember_the_milk/manifest.json b/homeassistant/components/remember_the_milk/manifest.json new file mode 100644 index 00000000000000..a2076eb5800de4 --- /dev/null +++ b/homeassistant/components/remember_the_milk/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "remember_the_milk", + "name": "Remember the milk", + "documentation": "https://www.home-assistant.io/components/remember_the_milk", + "requirements": [ + "RtmAPI==0.7.0", + "httplib2==0.10.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/remote/manifest.json b/homeassistant/components/remote/manifest.json new file mode 100644 index 00000000000000..5fe585dcd8346d --- /dev/null +++ b/homeassistant/components/remote/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "remote", + "name": "Remote", + "documentation": "https://www.home-assistant.io/components/remote", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/rest/manifest.json b/homeassistant/components/rest/manifest.json new file mode 100644 index 00000000000000..999f57407151d6 --- /dev/null +++ b/homeassistant/components/rest/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "rest", + "name": "Rest", + "documentation": "https://www.home-assistant.io/components/rest", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rest_command/manifest.json b/homeassistant/components/rest_command/manifest.json new file mode 100644 index 00000000000000..ced930fc64f5e2 --- /dev/null +++ b/homeassistant/components/rest_command/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "rest_command", + "name": "Rest command", + "documentation": "https://www.home-assistant.io/components/rest_command", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rflink/manifest.json b/homeassistant/components/rflink/manifest.json new file mode 100644 index 00000000000000..a3b81f39c55f71 --- /dev/null +++ b/homeassistant/components/rflink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rflink", + "name": "Rflink", + "documentation": "https://www.home-assistant.io/components/rflink", + "requirements": [ + "rflink==0.0.37" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rfxtrx/manifest.json b/homeassistant/components/rfxtrx/manifest.json new file mode 100644 index 00000000000000..5d6cd4b038cbb8 --- /dev/null +++ b/homeassistant/components/rfxtrx/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "rfxtrx", + "name": "Rfxtrx", + "documentation": "https://www.home-assistant.io/components/rfxtrx", + "requirements": [ + "pyRFXtrx==0.23" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/ring/manifest.json b/homeassistant/components/ring/manifest.json new file mode 100644 index 00000000000000..4d1fc62903553e --- /dev/null +++ b/homeassistant/components/ring/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ring", + "name": "Ring", + "documentation": "https://www.home-assistant.io/components/ring", + "requirements": [ + "ring_doorbell==0.2.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ripple/manifest.json b/homeassistant/components/ripple/manifest.json new file mode 100644 index 00000000000000..fe93bf02445dce --- /dev/null +++ b/homeassistant/components/ripple/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ripple", + "name": "Ripple", + "documentation": "https://www.home-assistant.io/components/ripple", + "requirements": [ + "python-ripple-api==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ritassist/manifest.json b/homeassistant/components/ritassist/manifest.json new file mode 100644 index 00000000000000..af8464e4e93e27 --- /dev/null +++ b/homeassistant/components/ritassist/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ritassist", + "name": "Ritassist", + "documentation": "https://www.home-assistant.io/components/ritassist", + "requirements": [ + "ritassist==0.9.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rmvtransport/manifest.json b/homeassistant/components/rmvtransport/manifest.json new file mode 100644 index 00000000000000..3f32a61c081fc5 --- /dev/null +++ b/homeassistant/components/rmvtransport/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "rmvtransport", + "name": "Rmvtransport", + "documentation": "https://www.home-assistant.io/components/rmvtransport", + "requirements": [ + "PyRMVtransport==0.1.3" + ], + "dependencies": [], + "codeowners": [ + "@cgtobi" + ] +} diff --git a/homeassistant/components/rocketchat/manifest.json b/homeassistant/components/rocketchat/manifest.json new file mode 100644 index 00000000000000..3a8959f1be61b5 --- /dev/null +++ b/homeassistant/components/rocketchat/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rocketchat", + "name": "Rocketchat", + "documentation": "https://www.home-assistant.io/components/rocketchat", + "requirements": [ + "rocketchat-API==0.6.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/roku/manifest.json b/homeassistant/components/roku/manifest.json new file mode 100644 index 00000000000000..7f7befbe418114 --- /dev/null +++ b/homeassistant/components/roku/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "roku", + "name": "Roku", + "documentation": "https://www.home-assistant.io/components/roku", + "requirements": [ + "python-roku==3.1.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/roomba/manifest.json b/homeassistant/components/roomba/manifest.json new file mode 100644 index 00000000000000..058ad0c5e815ac --- /dev/null +++ b/homeassistant/components/roomba/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "roomba", + "name": "Roomba", + "documentation": "https://www.home-assistant.io/components/roomba", + "requirements": [ + "roombapy==1.3.1" + ], + "dependencies": [], + "codeowners": [ + "@pschmitt" + ] +} diff --git a/homeassistant/components/route53/manifest.json b/homeassistant/components/route53/manifest.json new file mode 100644 index 00000000000000..d377ca7dca03c0 --- /dev/null +++ b/homeassistant/components/route53/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "route53", + "name": "Route53", + "documentation": "https://www.home-assistant.io/components/route53", + "requirements": [ + "boto3==1.9.16", + "ipify==1.0.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rova/manifest.json b/homeassistant/components/rova/manifest.json new file mode 100644 index 00000000000000..71ec8fcbc9b3ab --- /dev/null +++ b/homeassistant/components/rova/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rova", + "name": "Rova", + "documentation": "https://www.home-assistant.io/components/rova", + "requirements": [ + "rova==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rpi_camera/manifest.json b/homeassistant/components/rpi_camera/manifest.json new file mode 100644 index 00000000000000..1f905b103fed24 --- /dev/null +++ b/homeassistant/components/rpi_camera/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "rpi_camera", + "name": "Rpi camera", + "documentation": "https://www.home-assistant.io/components/rpi_camera", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rpi_gpio/manifest.json b/homeassistant/components/rpi_gpio/manifest.json new file mode 100644 index 00000000000000..88322708b27738 --- /dev/null +++ b/homeassistant/components/rpi_gpio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rpi_gpio", + "name": "Rpi gpio", + "documentation": "https://www.home-assistant.io/components/rpi_gpio", + "requirements": [ + "RPi.GPIO==0.6.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rpi_gpio_pwm/manifest.json b/homeassistant/components/rpi_gpio_pwm/manifest.json new file mode 100644 index 00000000000000..d2ed380d68ad0b --- /dev/null +++ b/homeassistant/components/rpi_gpio_pwm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rpi_gpio_pwm", + "name": "Rpi gpio pwm", + "documentation": "https://www.home-assistant.io/components/rpi_gpio_pwm", + "requirements": [ + "pwmled==1.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rpi_pfio/manifest.json b/homeassistant/components/rpi_pfio/manifest.json new file mode 100644 index 00000000000000..7fc724bf90a3ef --- /dev/null +++ b/homeassistant/components/rpi_pfio/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "rpi_pfio", + "name": "Rpi pfio", + "documentation": "https://www.home-assistant.io/components/rpi_pfio", + "requirements": [ + "pifacecommon==4.2.2", + "pifacedigitalio==3.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rpi_rf/manifest.json b/homeassistant/components/rpi_rf/manifest.json new file mode 100644 index 00000000000000..e5fffee131e337 --- /dev/null +++ b/homeassistant/components/rpi_rf/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rpi_rf", + "name": "Rpi rf", + "documentation": "https://www.home-assistant.io/components/rpi_rf", + "requirements": [ + "rpi-rf==0.9.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/rss_feed_template/manifest.json b/homeassistant/components/rss_feed_template/manifest.json new file mode 100644 index 00000000000000..c92f6b2a0bad3a --- /dev/null +++ b/homeassistant/components/rss_feed_template/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "rss_feed_template", + "name": "Rss feed template", + "documentation": "https://www.home-assistant.io/components/rss_feed_template", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/rtorrent/manifest.json b/homeassistant/components/rtorrent/manifest.json new file mode 100644 index 00000000000000..ce2dca9e0853d3 --- /dev/null +++ b/homeassistant/components/rtorrent/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "rtorrent", + "name": "Rtorrent", + "documentation": "https://www.home-assistant.io/components/rtorrent", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/russound_rio/manifest.json b/homeassistant/components/russound_rio/manifest.json new file mode 100644 index 00000000000000..af81d9c031a3ea --- /dev/null +++ b/homeassistant/components/russound_rio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "russound_rio", + "name": "Russound rio", + "documentation": "https://www.home-assistant.io/components/russound_rio", + "requirements": [ + "russound_rio==0.1.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/russound_rnet/manifest.json b/homeassistant/components/russound_rnet/manifest.json new file mode 100644 index 00000000000000..716f383040f6f1 --- /dev/null +++ b/homeassistant/components/russound_rnet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "russound_rnet", + "name": "Russound rnet", + "documentation": "https://www.home-assistant.io/components/russound_rnet", + "requirements": [ + "russound==0.1.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ruter/manifest.json b/homeassistant/components/ruter/manifest.json new file mode 100644 index 00000000000000..57688d0e025848 --- /dev/null +++ b/homeassistant/components/ruter/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ruter", + "name": "Ruter", + "documentation": "https://www.home-assistant.io/components/ruter", + "requirements": [ + "pyruter==1.1.0" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/sabnzbd/manifest.json b/homeassistant/components/sabnzbd/manifest.json new file mode 100644 index 00000000000000..ae03895f415b81 --- /dev/null +++ b/homeassistant/components/sabnzbd/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sabnzbd", + "name": "Sabnzbd", + "documentation": "https://www.home-assistant.io/components/sabnzbd", + "requirements": [ + "pysabnzbd==1.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json new file mode 100644 index 00000000000000..c8825f4ac3ff69 --- /dev/null +++ b/homeassistant/components/samsungtv/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "samsungtv", + "name": "Samsungtv", + "documentation": "https://www.home-assistant.io/components/samsungtv", + "requirements": [ + "samsungctl[websocket]==0.7.1", + "wakeonlan==1.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/satel_integra/manifest.json b/homeassistant/components/satel_integra/manifest.json new file mode 100644 index 00000000000000..8df19ed90de935 --- /dev/null +++ b/homeassistant/components/satel_integra/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "satel_integra", + "name": "Satel integra", + "documentation": "https://www.home-assistant.io/components/satel_integra", + "requirements": [ + "satel_integra==0.3.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/scene/manifest.json b/homeassistant/components/scene/manifest.json new file mode 100644 index 00000000000000..e1becfd1936423 --- /dev/null +++ b/homeassistant/components/scene/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "scene", + "name": "Scene", + "documentation": "https://www.home-assistant.io/components/scene", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/scrape/manifest.json b/homeassistant/components/scrape/manifest.json new file mode 100644 index 00000000000000..c7e60140dbf9c9 --- /dev/null +++ b/homeassistant/components/scrape/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "scrape", + "name": "Scrape", + "documentation": "https://www.home-assistant.io/components/scrape", + "requirements": [ + "beautifulsoup4==4.7.1" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/script/manifest.json b/homeassistant/components/script/manifest.json new file mode 100644 index 00000000000000..56a3c39b7b6b76 --- /dev/null +++ b/homeassistant/components/script/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "script", + "name": "Script", + "documentation": "https://www.home-assistant.io/components/script", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/scsgate/manifest.json b/homeassistant/components/scsgate/manifest.json new file mode 100644 index 00000000000000..d565a5d336d5d3 --- /dev/null +++ b/homeassistant/components/scsgate/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "scsgate", + "name": "Scsgate", + "documentation": "https://www.home-assistant.io/components/scsgate", + "requirements": [ + "scsgate==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/season/manifest.json b/homeassistant/components/season/manifest.json new file mode 100644 index 00000000000000..74bdb3d8b883a9 --- /dev/null +++ b/homeassistant/components/season/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "season", + "name": "Season", + "documentation": "https://www.home-assistant.io/components/season", + "requirements": [ + "ephem==3.7.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sendgrid/manifest.json b/homeassistant/components/sendgrid/manifest.json new file mode 100644 index 00000000000000..47372d861f1129 --- /dev/null +++ b/homeassistant/components/sendgrid/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sendgrid", + "name": "Sendgrid", + "documentation": "https://www.home-assistant.io/components/sendgrid", + "requirements": [ + "sendgrid==5.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sense/manifest.json b/homeassistant/components/sense/manifest.json new file mode 100644 index 00000000000000..272a4a58f33835 --- /dev/null +++ b/homeassistant/components/sense/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sense", + "name": "Sense", + "documentation": "https://www.home-assistant.io/components/sense", + "requirements": [ + "sense_energy==0.7.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sensehat/manifest.json b/homeassistant/components/sensehat/manifest.json new file mode 100644 index 00000000000000..cb148c92198875 --- /dev/null +++ b/homeassistant/components/sensehat/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sensehat", + "name": "Sensehat", + "documentation": "https://www.home-assistant.io/components/sensehat", + "requirements": [ + "sense-hat==2.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json new file mode 100644 index 00000000000000..776b8444b82276 --- /dev/null +++ b/homeassistant/components/sensibo/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sensibo", + "name": "Sensibo", + "documentation": "https://www.home-assistant.io/components/sensibo", + "requirements": [ + "pysensibo==1.0.3" + ], + "dependencies": [], + "codeowners": [ + "@andrey-git" + ] +} diff --git a/homeassistant/components/sensor/manifest.json b/homeassistant/components/sensor/manifest.json new file mode 100644 index 00000000000000..813bcc27acaf5d --- /dev/null +++ b/homeassistant/components/sensor/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "sensor", + "name": "Sensor", + "documentation": "https://www.home-assistant.io/components/sensor", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/serial/manifest.json b/homeassistant/components/serial/manifest.json new file mode 100644 index 00000000000000..945464dbdec027 --- /dev/null +++ b/homeassistant/components/serial/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "serial", + "name": "Serial", + "documentation": "https://www.home-assistant.io/components/serial", + "requirements": [ + "pyserial-asyncio==0.4" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/serial_pm/manifest.json b/homeassistant/components/serial_pm/manifest.json new file mode 100644 index 00000000000000..b2a645c88f3c87 --- /dev/null +++ b/homeassistant/components/serial_pm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "serial_pm", + "name": "Serial pm", + "documentation": "https://www.home-assistant.io/components/serial_pm", + "requirements": [ + "pmsensor==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sesame/manifest.json b/homeassistant/components/sesame/manifest.json new file mode 100644 index 00000000000000..9aed47462fe1cd --- /dev/null +++ b/homeassistant/components/sesame/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sesame", + "name": "Sesame", + "documentation": "https://www.home-assistant.io/components/sesame", + "requirements": [ + "pysesame==0.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/seven_segments/manifest.json b/homeassistant/components/seven_segments/manifest.json new file mode 100644 index 00000000000000..45ce2f6a7a00c2 --- /dev/null +++ b/homeassistant/components/seven_segments/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "seven_segments", + "name": "Seven segments", + "documentation": "https://www.home-assistant.io/components/seven_segments", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/seventeentrack/manifest.json b/homeassistant/components/seventeentrack/manifest.json new file mode 100644 index 00000000000000..47b36c122913a2 --- /dev/null +++ b/homeassistant/components/seventeentrack/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "seventeentrack", + "name": "Seventeentrack", + "documentation": "https://www.home-assistant.io/components/seventeentrack", + "requirements": [ + "py17track==2.2.2" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/shell_command/manifest.json b/homeassistant/components/shell_command/manifest.json new file mode 100644 index 00000000000000..dfe9a8e8e6fb5b --- /dev/null +++ b/homeassistant/components/shell_command/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "shell_command", + "name": "Shell command", + "documentation": "https://www.home-assistant.io/components/shell_command", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/shiftr/manifest.json b/homeassistant/components/shiftr/manifest.json new file mode 100644 index 00000000000000..02718396e5e64c --- /dev/null +++ b/homeassistant/components/shiftr/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "shiftr", + "name": "Shiftr", + "documentation": "https://www.home-assistant.io/components/shiftr", + "requirements": [ + "paho-mqtt==1.4.0" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/shodan/manifest.json b/homeassistant/components/shodan/manifest.json new file mode 100644 index 00000000000000..8898b7976b5d52 --- /dev/null +++ b/homeassistant/components/shodan/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "shodan", + "name": "Shodan", + "documentation": "https://www.home-assistant.io/components/shodan", + "requirements": [ + "shodan==1.11.1" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/shopping_list/manifest.json b/homeassistant/components/shopping_list/manifest.json new file mode 100644 index 00000000000000..b4ea3c2d38816a --- /dev/null +++ b/homeassistant/components/shopping_list/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "shopping_list", + "name": "Shopping list", + "documentation": "https://www.home-assistant.io/components/shopping_list", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/sht31/manifest.json b/homeassistant/components/sht31/manifest.json new file mode 100644 index 00000000000000..dfa22fc6e23b4a --- /dev/null +++ b/homeassistant/components/sht31/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "sht31", + "name": "Sht31", + "documentation": "https://www.home-assistant.io/components/sht31", + "requirements": [ + "Adafruit-GPIO==1.0.3", + "Adafruit-SHT31==1.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sigfox/manifest.json b/homeassistant/components/sigfox/manifest.json new file mode 100644 index 00000000000000..1dc8f5255cea1d --- /dev/null +++ b/homeassistant/components/sigfox/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "sigfox", + "name": "Sigfox", + "documentation": "https://www.home-assistant.io/components/sigfox", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/simplepush/manifest.json b/homeassistant/components/simplepush/manifest.json new file mode 100644 index 00000000000000..cbf2833a4f765b --- /dev/null +++ b/homeassistant/components/simplepush/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "simplepush", + "name": "Simplepush", + "documentation": "https://www.home-assistant.io/components/simplepush", + "requirements": [ + "simplepush==1.1.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json new file mode 100644 index 00000000000000..eac586b355d789 --- /dev/null +++ b/homeassistant/components/simplisafe/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "simplisafe", + "name": "Simplisafe", + "documentation": "https://www.home-assistant.io/components/simplisafe", + "requirements": [ + "simplisafe-python==3.4.1" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/simulated/manifest.json b/homeassistant/components/simulated/manifest.json new file mode 100644 index 00000000000000..b972152aea4c9d --- /dev/null +++ b/homeassistant/components/simulated/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "simulated", + "name": "Simulated", + "documentation": "https://www.home-assistant.io/components/simulated", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sisyphus/manifest.json b/homeassistant/components/sisyphus/manifest.json new file mode 100644 index 00000000000000..b1809e7a57211f --- /dev/null +++ b/homeassistant/components/sisyphus/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sisyphus", + "name": "Sisyphus", + "documentation": "https://www.home-assistant.io/components/sisyphus", + "requirements": [ + "sisyphus-control==2.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sky_hub/manifest.json b/homeassistant/components/sky_hub/manifest.json new file mode 100644 index 00000000000000..46337918f84bcc --- /dev/null +++ b/homeassistant/components/sky_hub/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "sky_hub", + "name": "Sky hub", + "documentation": "https://www.home-assistant.io/components/sky_hub", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/skybeacon/manifest.json b/homeassistant/components/skybeacon/manifest.json new file mode 100644 index 00000000000000..8d2f758aed22bb --- /dev/null +++ b/homeassistant/components/skybeacon/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "skybeacon", + "name": "Skybeacon", + "documentation": "https://www.home-assistant.io/components/skybeacon", + "requirements": [ + "pygatt[GATTTOOL]==3.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/skybell/manifest.json b/homeassistant/components/skybell/manifest.json new file mode 100644 index 00000000000000..6a22a698b4ca2b --- /dev/null +++ b/homeassistant/components/skybell/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "skybell", + "name": "Skybell", + "documentation": "https://www.home-assistant.io/components/skybell", + "requirements": [ + "skybellpy==0.3.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/slack/manifest.json b/homeassistant/components/slack/manifest.json new file mode 100644 index 00000000000000..3b6e764f814188 --- /dev/null +++ b/homeassistant/components/slack/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "slack", + "name": "Slack", + "documentation": "https://www.home-assistant.io/components/slack", + "requirements": [ + "slacker==0.12.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sleepiq/manifest.json b/homeassistant/components/sleepiq/manifest.json new file mode 100644 index 00000000000000..339685d32e1ee6 --- /dev/null +++ b/homeassistant/components/sleepiq/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sleepiq", + "name": "Sleepiq", + "documentation": "https://www.home-assistant.io/components/sleepiq", + "requirements": [ + "sleepyq==0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sma/manifest.json b/homeassistant/components/sma/manifest.json new file mode 100644 index 00000000000000..e5e7a5bf44643e --- /dev/null +++ b/homeassistant/components/sma/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sma", + "name": "Sma", + "documentation": "https://www.home-assistant.io/components/sma", + "requirements": [ + "pysma==0.3.1" + ], + "dependencies": [], + "codeowners": [ + "@kellerza" + ] +} diff --git a/homeassistant/components/smappee/manifest.json b/homeassistant/components/smappee/manifest.json new file mode 100644 index 00000000000000..361802f312e46a --- /dev/null +++ b/homeassistant/components/smappee/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "smappee", + "name": "Smappee", + "documentation": "https://www.home-assistant.io/components/smappee", + "requirements": [ + "smappy==0.2.16" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/smartthings/manifest.json b/homeassistant/components/smartthings/manifest.json new file mode 100644 index 00000000000000..d4baf69b10890f --- /dev/null +++ b/homeassistant/components/smartthings/manifest.json @@ -0,0 +1,15 @@ +{ + "domain": "smartthings", + "name": "Smartthings", + "documentation": "https://www.home-assistant.io/components/smartthings", + "requirements": [ + "pysmartapp==0.3.2", + "pysmartthings==0.6.7" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [ + "@andrewsayre" + ] +} diff --git a/homeassistant/components/smhi/manifest.json b/homeassistant/components/smhi/manifest.json new file mode 100644 index 00000000000000..e4ad478e0339d9 --- /dev/null +++ b/homeassistant/components/smhi/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "smhi", + "name": "Smhi", + "documentation": "https://www.home-assistant.io/components/smhi", + "requirements": [ + "smhi-pkg==1.0.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/smtp/manifest.json b/homeassistant/components/smtp/manifest.json new file mode 100644 index 00000000000000..2e1a8d826ce53d --- /dev/null +++ b/homeassistant/components/smtp/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "smtp", + "name": "Smtp", + "documentation": "https://www.home-assistant.io/components/smtp", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/snapcast/manifest.json b/homeassistant/components/snapcast/manifest.json new file mode 100644 index 00000000000000..70c9db7dada38e --- /dev/null +++ b/homeassistant/components/snapcast/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "snapcast", + "name": "Snapcast", + "documentation": "https://www.home-assistant.io/components/snapcast", + "requirements": [ + "snapcast==2.0.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/snips/manifest.json b/homeassistant/components/snips/manifest.json new file mode 100644 index 00000000000000..58fddb7a3f4ef4 --- /dev/null +++ b/homeassistant/components/snips/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "snips", + "name": "Snips", + "documentation": "https://www.home-assistant.io/components/snips", + "requirements": [], + "dependencies": [ + "mqtt" + ], + "codeowners": [] +} diff --git a/homeassistant/components/snmp/manifest.json b/homeassistant/components/snmp/manifest.json new file mode 100644 index 00000000000000..aeaa3451683c6b --- /dev/null +++ b/homeassistant/components/snmp/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "snmp", + "name": "Snmp", + "documentation": "https://www.home-assistant.io/components/snmp", + "requirements": [ + "pysnmp==4.4.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sochain/manifest.json b/homeassistant/components/sochain/manifest.json new file mode 100644 index 00000000000000..23fad3683cb419 --- /dev/null +++ b/homeassistant/components/sochain/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sochain", + "name": "Sochain", + "documentation": "https://www.home-assistant.io/components/sochain", + "requirements": [ + "python-sochain-api==0.0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/socialblade/manifest.json b/homeassistant/components/socialblade/manifest.json new file mode 100644 index 00000000000000..e800bd7266a0ba --- /dev/null +++ b/homeassistant/components/socialblade/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "socialblade", + "name": "Socialblade", + "documentation": "https://www.home-assistant.io/components/socialblade", + "requirements": [ + "socialbladeclient==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/solaredge/manifest.json b/homeassistant/components/solaredge/manifest.json new file mode 100644 index 00000000000000..b2707a0a9372de --- /dev/null +++ b/homeassistant/components/solaredge/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "solaredge", + "name": "Solaredge", + "documentation": "https://www.home-assistant.io/components/solaredge", + "requirements": [ + "solaredge==0.0.2", + "stringcase==1.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sonarr/manifest.json b/homeassistant/components/sonarr/manifest.json new file mode 100644 index 00000000000000..bc0235ec5b3dbc --- /dev/null +++ b/homeassistant/components/sonarr/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "sonarr", + "name": "Sonarr", + "documentation": "https://www.home-assistant.io/components/sonarr", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/songpal/manifest.json b/homeassistant/components/songpal/manifest.json new file mode 100644 index 00000000000000..0d1af7053b29b9 --- /dev/null +++ b/homeassistant/components/songpal/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "songpal", + "name": "Songpal", + "documentation": "https://www.home-assistant.io/components/songpal", + "requirements": [ + "python-songpal==0.0.9.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json new file mode 100644 index 00000000000000..3fa5ac0354a6da --- /dev/null +++ b/homeassistant/components/sonos/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sonos", + "name": "Sonos", + "documentation": "https://www.home-assistant.io/components/sonos", + "requirements": [ + "pysonos==0.0.8" + ], + "dependencies": [], + "codeowners": [ + "@amelchio" + ] +} diff --git a/homeassistant/components/sony_projector/manifest.json b/homeassistant/components/sony_projector/manifest.json new file mode 100644 index 00000000000000..1cc25d93f59863 --- /dev/null +++ b/homeassistant/components/sony_projector/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sony_projector", + "name": "Sony projector", + "documentation": "https://www.home-assistant.io/components/sony_projector", + "requirements": [ + "pysdcp==1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/soundtouch/manifest.json b/homeassistant/components/soundtouch/manifest.json new file mode 100644 index 00000000000000..eba60bc6e34693 --- /dev/null +++ b/homeassistant/components/soundtouch/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "soundtouch", + "name": "Soundtouch", + "documentation": "https://www.home-assistant.io/components/soundtouch", + "requirements": [ + "libsoundtouch==0.7.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/spaceapi/manifest.json b/homeassistant/components/spaceapi/manifest.json new file mode 100644 index 00000000000000..03aa5c0a1f7e2b --- /dev/null +++ b/homeassistant/components/spaceapi/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "spaceapi", + "name": "Spaceapi", + "documentation": "https://www.home-assistant.io/components/spaceapi", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/spc/manifest.json b/homeassistant/components/spc/manifest.json new file mode 100644 index 00000000000000..572d4b04b87cf9 --- /dev/null +++ b/homeassistant/components/spc/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "spc", + "name": "Spc", + "documentation": "https://www.home-assistant.io/components/spc", + "requirements": [ + "pyspcwebgw==0.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/speedtestdotnet/manifest.json b/homeassistant/components/speedtestdotnet/manifest.json new file mode 100644 index 00000000000000..91b7e7c5c0fcff --- /dev/null +++ b/homeassistant/components/speedtestdotnet/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "speedtestdotnet", + "name": "Speedtestdotnet", + "documentation": "https://www.home-assistant.io/components/speedtestdotnet", + "requirements": [ + "speedtest-cli==2.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/spider/manifest.json b/homeassistant/components/spider/manifest.json new file mode 100644 index 00000000000000..4cd7a4677370a8 --- /dev/null +++ b/homeassistant/components/spider/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "spider", + "name": "Spider", + "documentation": "https://www.home-assistant.io/components/spider", + "requirements": [ + "spiderpy==1.3.1" + ], + "dependencies": [], + "codeowners": [ + "@peternijssen" + ] +} diff --git a/homeassistant/components/splunk/manifest.json b/homeassistant/components/splunk/manifest.json new file mode 100644 index 00000000000000..2e81da3409a6fc --- /dev/null +++ b/homeassistant/components/splunk/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "splunk", + "name": "Splunk", + "documentation": "https://www.home-assistant.io/components/splunk", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/spotcrime/manifest.json b/homeassistant/components/spotcrime/manifest.json new file mode 100644 index 00000000000000..49b8742c53e1bc --- /dev/null +++ b/homeassistant/components/spotcrime/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "spotcrime", + "name": "Spotcrime", + "documentation": "https://www.home-assistant.io/components/spotcrime", + "requirements": [ + "spotcrime==1.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json new file mode 100644 index 00000000000000..8c2c72e4d2a157 --- /dev/null +++ b/homeassistant/components/spotify/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "spotify", + "name": "Spotify", + "documentation": "https://www.home-assistant.io/components/spotify", + "requirements": [ + "spotipy-homeassistant==2.4.4.dev1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sql/manifest.json b/homeassistant/components/sql/manifest.json new file mode 100644 index 00000000000000..9a26e6760185a7 --- /dev/null +++ b/homeassistant/components/sql/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sql", + "name": "Sql", + "documentation": "https://www.home-assistant.io/components/sql", + "requirements": [ + "sqlalchemy==1.3.0" + ], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/squeezebox/manifest.json b/homeassistant/components/squeezebox/manifest.json new file mode 100644 index 00000000000000..ae124d6c03d517 --- /dev/null +++ b/homeassistant/components/squeezebox/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "squeezebox", + "name": "Squeezebox", + "documentation": "https://www.home-assistant.io/components/squeezebox", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/srp_energy/manifest.json b/homeassistant/components/srp_energy/manifest.json new file mode 100644 index 00000000000000..050a78223c17f5 --- /dev/null +++ b/homeassistant/components/srp_energy/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "srp_energy", + "name": "Srp energy", + "documentation": "https://www.home-assistant.io/components/srp_energy", + "requirements": [ + "srpenergy==1.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/starlingbank/manifest.json b/homeassistant/components/starlingbank/manifest.json new file mode 100644 index 00000000000000..1314fda5099fba --- /dev/null +++ b/homeassistant/components/starlingbank/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "starlingbank", + "name": "Starlingbank", + "documentation": "https://www.home-assistant.io/components/starlingbank", + "requirements": [ + "starlingbank==3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/startca/manifest.json b/homeassistant/components/startca/manifest.json new file mode 100644 index 00000000000000..1d13936f592c1c --- /dev/null +++ b/homeassistant/components/startca/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "startca", + "name": "Startca", + "documentation": "https://www.home-assistant.io/components/startca", + "requirements": [ + "xmltodict==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/statistics/manifest.json b/homeassistant/components/statistics/manifest.json new file mode 100644 index 00000000000000..49e476a687632b --- /dev/null +++ b/homeassistant/components/statistics/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "statistics", + "name": "Statistics", + "documentation": "https://www.home-assistant.io/components/statistics", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/statsd/manifest.json b/homeassistant/components/statsd/manifest.json new file mode 100644 index 00000000000000..20f4cc7f5443a6 --- /dev/null +++ b/homeassistant/components/statsd/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "statsd", + "name": "Statsd", + "documentation": "https://www.home-assistant.io/components/statsd", + "requirements": [ + "statsd==3.2.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/steam_online/manifest.json b/homeassistant/components/steam_online/manifest.json new file mode 100644 index 00000000000000..735a1869c34d86 --- /dev/null +++ b/homeassistant/components/steam_online/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "steam_online", + "name": "Steam online", + "documentation": "https://www.home-assistant.io/components/steam_online", + "requirements": [ + "steamodd==4.21" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/stream/manifest.json b/homeassistant/components/stream/manifest.json new file mode 100644 index 00000000000000..9020ffb5b2bd98 --- /dev/null +++ b/homeassistant/components/stream/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "stream", + "name": "Stream", + "documentation": "https://www.home-assistant.io/components/stream", + "requirements": [ + "av==6.1.2" + ], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/stride/manifest.json b/homeassistant/components/stride/manifest.json new file mode 100644 index 00000000000000..307f4c929cfb4e --- /dev/null +++ b/homeassistant/components/stride/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "stride", + "name": "Stride", + "documentation": "https://www.home-assistant.io/components/stride", + "requirements": [ + "pystride==0.1.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sun/manifest.json b/homeassistant/components/sun/manifest.json new file mode 100644 index 00000000000000..2ef89da8f69a79 --- /dev/null +++ b/homeassistant/components/sun/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "sun", + "name": "Sun", + "documentation": "https://www.home-assistant.io/components/sun", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/supervisord/manifest.json b/homeassistant/components/supervisord/manifest.json new file mode 100644 index 00000000000000..1fc849165ef00c --- /dev/null +++ b/homeassistant/components/supervisord/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "supervisord", + "name": "Supervisord", + "documentation": "https://www.home-assistant.io/components/supervisord", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/swiss_hydrological_data/manifest.json b/homeassistant/components/swiss_hydrological_data/manifest.json new file mode 100644 index 00000000000000..d6b18d6cba80a0 --- /dev/null +++ b/homeassistant/components/swiss_hydrological_data/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "swiss_hydrological_data", + "name": "Swiss hydrological data", + "documentation": "https://www.home-assistant.io/components/swiss_hydrological_data", + "requirements": [ + "swisshydrodata==0.0.3" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/swiss_public_transport/manifest.json b/homeassistant/components/swiss_public_transport/manifest.json new file mode 100644 index 00000000000000..99dcdbd0c882c7 --- /dev/null +++ b/homeassistant/components/swiss_public_transport/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "swiss_public_transport", + "name": "Swiss public transport", + "documentation": "https://www.home-assistant.io/components/swiss_public_transport", + "requirements": [ + "python_opendata_transport==0.1.4" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/swisscom/manifest.json b/homeassistant/components/swisscom/manifest.json new file mode 100644 index 00000000000000..e52fda3408395f --- /dev/null +++ b/homeassistant/components/swisscom/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "swisscom", + "name": "Swisscom", + "documentation": "https://www.home-assistant.io/components/swisscom", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/switch/manifest.json b/homeassistant/components/switch/manifest.json new file mode 100644 index 00000000000000..0f2872515827ac --- /dev/null +++ b/homeassistant/components/switch/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "switch", + "name": "Switch", + "documentation": "https://www.home-assistant.io/components/switch", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json new file mode 100644 index 00000000000000..0143855db37c8e --- /dev/null +++ b/homeassistant/components/switchbot/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "switchbot", + "name": "Switchbot", + "documentation": "https://www.home-assistant.io/components/switchbot", + "requirements": [ + "PySwitchbot==0.5" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/switchmate/manifest.json b/homeassistant/components/switchmate/manifest.json new file mode 100644 index 00000000000000..9461c776d6d60d --- /dev/null +++ b/homeassistant/components/switchmate/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "switchmate", + "name": "Switchmate", + "documentation": "https://www.home-assistant.io/components/switchmate", + "requirements": [ + "pySwitchmate==0.4.5" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/syncthru/manifest.json b/homeassistant/components/syncthru/manifest.json new file mode 100644 index 00000000000000..1aadeb549096bd --- /dev/null +++ b/homeassistant/components/syncthru/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "syncthru", + "name": "Syncthru", + "documentation": "https://www.home-assistant.io/components/syncthru", + "requirements": [ + "pysyncthru==0.3.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/synology/manifest.json b/homeassistant/components/synology/manifest.json new file mode 100644 index 00000000000000..a108f5fa98352b --- /dev/null +++ b/homeassistant/components/synology/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "synology", + "name": "Synology", + "documentation": "https://www.home-assistant.io/components/synology", + "requirements": [ + "py-synology==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/synology_chat/manifest.json b/homeassistant/components/synology_chat/manifest.json new file mode 100644 index 00000000000000..d35b1d8c902302 --- /dev/null +++ b/homeassistant/components/synology_chat/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "synology_chat", + "name": "Synology chat", + "documentation": "https://www.home-assistant.io/components/synology_chat", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/synology_srm/manifest.json b/homeassistant/components/synology_srm/manifest.json new file mode 100644 index 00000000000000..fa89577f26ee7e --- /dev/null +++ b/homeassistant/components/synology_srm/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "synology_srm", + "name": "Synology srm", + "documentation": "https://www.home-assistant.io/components/synology_srm", + "requirements": [ + "synology-srm==0.0.6" + ], + "dependencies": [], + "codeowners": [ + "@aerialls" + ] +} diff --git a/homeassistant/components/synologydsm/manifest.json b/homeassistant/components/synologydsm/manifest.json new file mode 100644 index 00000000000000..fcce2e52a215d4 --- /dev/null +++ b/homeassistant/components/synologydsm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "synologydsm", + "name": "Synologydsm", + "documentation": "https://www.home-assistant.io/components/synologydsm", + "requirements": [ + "python-synology==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/syslog/manifest.json b/homeassistant/components/syslog/manifest.json new file mode 100644 index 00000000000000..19836ffa67f094 --- /dev/null +++ b/homeassistant/components/syslog/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "syslog", + "name": "Syslog", + "documentation": "https://www.home-assistant.io/components/syslog", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/system_health/manifest.json b/homeassistant/components/system_health/manifest.json new file mode 100644 index 00000000000000..9c2b7bcae39c2e --- /dev/null +++ b/homeassistant/components/system_health/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "system_health", + "name": "System health", + "documentation": "https://www.home-assistant.io/components/system_health", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/system_log/manifest.json b/homeassistant/components/system_log/manifest.json new file mode 100644 index 00000000000000..01f70af4a15c36 --- /dev/null +++ b/homeassistant/components/system_log/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "system_log", + "name": "System log", + "documentation": "https://www.home-assistant.io/components/system_log", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/systemmonitor/manifest.json b/homeassistant/components/systemmonitor/manifest.json new file mode 100644 index 00000000000000..591e710a871dff --- /dev/null +++ b/homeassistant/components/systemmonitor/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "systemmonitor", + "name": "Systemmonitor", + "documentation": "https://www.home-assistant.io/components/systemmonitor", + "requirements": [ + "psutil==5.6.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/sytadin/manifest.json b/homeassistant/components/sytadin/manifest.json new file mode 100644 index 00000000000000..0efc84fc5529e4 --- /dev/null +++ b/homeassistant/components/sytadin/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sytadin", + "name": "Sytadin", + "documentation": "https://www.home-assistant.io/components/sytadin", + "requirements": [ + "beautifulsoup4==4.7.1" + ], + "dependencies": [], + "codeowners": [ + "@gautric" + ] +} diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json new file mode 100644 index 00000000000000..8d42cde1c05162 --- /dev/null +++ b/homeassistant/components/tado/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tado", + "name": "Tado", + "documentation": "https://www.home-assistant.io/components/tado", + "requirements": [ + "python-tado==0.2.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tahoma/manifest.json b/homeassistant/components/tahoma/manifest.json new file mode 100644 index 00000000000000..ca3ab0bc882e21 --- /dev/null +++ b/homeassistant/components/tahoma/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tahoma", + "name": "Tahoma", + "documentation": "https://www.home-assistant.io/components/tahoma", + "requirements": [ + "tahoma-api==0.0.14" + ], + "dependencies": [], + "codeowners": [ + "@philklei" + ] +} diff --git a/homeassistant/components/tank_utility/manifest.json b/homeassistant/components/tank_utility/manifest.json new file mode 100644 index 00000000000000..04ffb48f39656c --- /dev/null +++ b/homeassistant/components/tank_utility/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tank_utility", + "name": "Tank utility", + "documentation": "https://www.home-assistant.io/components/tank_utility", + "requirements": [ + "tank_utility==1.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tapsaff/manifest.json b/homeassistant/components/tapsaff/manifest.json new file mode 100644 index 00000000000000..6008ef38cc6cbe --- /dev/null +++ b/homeassistant/components/tapsaff/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tapsaff", + "name": "Tapsaff", + "documentation": "https://www.home-assistant.io/components/tapsaff", + "requirements": [ + "tapsaff==0.2.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tautulli/manifest.json b/homeassistant/components/tautulli/manifest.json new file mode 100644 index 00000000000000..d49b52801813d8 --- /dev/null +++ b/homeassistant/components/tautulli/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tautulli", + "name": "Tautulli", + "documentation": "https://www.home-assistant.io/components/tautulli", + "requirements": [ + "pytautulli==0.5.0" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/tcp/manifest.json b/homeassistant/components/tcp/manifest.json new file mode 100644 index 00000000000000..2ff29a27f3169b --- /dev/null +++ b/homeassistant/components/tcp/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "tcp", + "name": "Tcp", + "documentation": "https://www.home-assistant.io/components/tcp", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ted5000/manifest.json b/homeassistant/components/ted5000/manifest.json new file mode 100644 index 00000000000000..cf0439345dc360 --- /dev/null +++ b/homeassistant/components/ted5000/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ted5000", + "name": "Ted5000", + "documentation": "https://www.home-assistant.io/components/ted5000", + "requirements": [ + "xmltodict==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/teksavvy/manifest.json b/homeassistant/components/teksavvy/manifest.json new file mode 100644 index 00000000000000..14afdec3b71551 --- /dev/null +++ b/homeassistant/components/teksavvy/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "teksavvy", + "name": "Teksavvy", + "documentation": "https://www.home-assistant.io/components/teksavvy", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/telegram/manifest.json b/homeassistant/components/telegram/manifest.json new file mode 100644 index 00000000000000..6ace3cd5aa0930 --- /dev/null +++ b/homeassistant/components/telegram/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "telegram", + "name": "Telegram", + "documentation": "https://www.home-assistant.io/components/telegram", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/telegram_bot/manifest.json b/homeassistant/components/telegram_bot/manifest.json new file mode 100644 index 00000000000000..ba52cd4e935d50 --- /dev/null +++ b/homeassistant/components/telegram_bot/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "telegram_bot", + "name": "Telegram bot", + "documentation": "https://www.home-assistant.io/components/telegram_bot", + "requirements": [ + "python-telegram-bot==11.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tellduslive/manifest.json b/homeassistant/components/tellduslive/manifest.json new file mode 100644 index 00000000000000..2e6233f426c526 --- /dev/null +++ b/homeassistant/components/tellduslive/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tellduslive", + "name": "Tellduslive", + "documentation": "https://www.home-assistant.io/components/tellduslive", + "requirements": [ + "tellduslive==0.10.10" + ], + "dependencies": [], + "codeowners": [ + "@fredrike" + ] +} diff --git a/homeassistant/components/tellstick/manifest.json b/homeassistant/components/tellstick/manifest.json new file mode 100644 index 00000000000000..c50ba514f2aaab --- /dev/null +++ b/homeassistant/components/tellstick/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "tellstick", + "name": "Tellstick", + "documentation": "https://www.home-assistant.io/components/tellstick", + "requirements": [ + "tellcore-net==0.4", + "tellcore-py==1.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/telnet/manifest.json b/homeassistant/components/telnet/manifest.json new file mode 100644 index 00000000000000..58f5e15cc1a37a --- /dev/null +++ b/homeassistant/components/telnet/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "telnet", + "name": "Telnet", + "documentation": "https://www.home-assistant.io/components/telnet", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/temper/manifest.json b/homeassistant/components/temper/manifest.json new file mode 100644 index 00000000000000..0e60c957d9d6a9 --- /dev/null +++ b/homeassistant/components/temper/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "temper", + "name": "Temper", + "documentation": "https://www.home-assistant.io/components/temper", + "requirements": [ + "temperusb==1.5.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/template/manifest.json b/homeassistant/components/template/manifest.json new file mode 100644 index 00000000000000..c8406c9d08494c --- /dev/null +++ b/homeassistant/components/template/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "template", + "name": "Template", + "documentation": "https://www.home-assistant.io/components/template", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@PhracturedBlue" + ] +} diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json new file mode 100644 index 00000000000000..e9643f36b679eb --- /dev/null +++ b/homeassistant/components/tensorflow/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tensorflow", + "name": "Tensorflow", + "documentation": "https://www.home-assistant.io/components/tensorflow", + "requirements": [ + "numpy==1.16.2", + "pillow==5.4.1", + "protobuf==3.6.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tesla/manifest.json b/homeassistant/components/tesla/manifest.json new file mode 100644 index 00000000000000..ab32a64e670f25 --- /dev/null +++ b/homeassistant/components/tesla/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tesla", + "name": "Tesla", + "documentation": "https://www.home-assistant.io/components/tesla", + "requirements": [ + "teslajsonpy==0.0.25" + ], + "dependencies": [], + "codeowners": [ + "@zabuldon" + ] +} diff --git a/homeassistant/components/tfiac/manifest.json b/homeassistant/components/tfiac/manifest.json new file mode 100644 index 00000000000000..9997ae00f0a4f4 --- /dev/null +++ b/homeassistant/components/tfiac/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "tfiac", + "name": "Tfiac", + "documentation": "https://www.home-assistant.io/components/tfiac", + "requirements": [ + "pytfiac==0.3" + ], + "dependencies": [], + "codeowners": [ + "@fredrike", + "@mellado" + ] +} diff --git a/homeassistant/components/thermoworks_smoke/manifest.json b/homeassistant/components/thermoworks_smoke/manifest.json new file mode 100644 index 00000000000000..fab670627ba891 --- /dev/null +++ b/homeassistant/components/thermoworks_smoke/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "thermoworks_smoke", + "name": "Thermoworks smoke", + "documentation": "https://www.home-assistant.io/components/thermoworks_smoke", + "requirements": [ + "stringcase==1.2.0", + "thermoworks_smoke==0.1.8" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/thethingsnetwork/manifest.json b/homeassistant/components/thethingsnetwork/manifest.json new file mode 100644 index 00000000000000..8d6082d74bfbb8 --- /dev/null +++ b/homeassistant/components/thethingsnetwork/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "thethingsnetwork", + "name": "Thethingsnetwork", + "documentation": "https://www.home-assistant.io/components/thethingsnetwork", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/thingspeak/manifest.json b/homeassistant/components/thingspeak/manifest.json new file mode 100644 index 00000000000000..482bb94ac2ae0a --- /dev/null +++ b/homeassistant/components/thingspeak/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "thingspeak", + "name": "Thingspeak", + "documentation": "https://www.home-assistant.io/components/thingspeak", + "requirements": [ + "thingspeak==0.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/thinkingcleaner/manifest.json b/homeassistant/components/thinkingcleaner/manifest.json new file mode 100644 index 00000000000000..4e43270a5e04ef --- /dev/null +++ b/homeassistant/components/thinkingcleaner/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "thinkingcleaner", + "name": "Thinkingcleaner", + "documentation": "https://www.home-assistant.io/components/thinkingcleaner", + "requirements": [ + "pythinkingcleaner==0.0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/thomson/manifest.json b/homeassistant/components/thomson/manifest.json new file mode 100644 index 00000000000000..063c84d4ff7e8f --- /dev/null +++ b/homeassistant/components/thomson/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "thomson", + "name": "Thomson", + "documentation": "https://www.home-assistant.io/components/thomson", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/threshold/manifest.json b/homeassistant/components/threshold/manifest.json new file mode 100644 index 00000000000000..107b4351505a7f --- /dev/null +++ b/homeassistant/components/threshold/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "threshold", + "name": "Threshold", + "documentation": "https://www.home-assistant.io/components/threshold", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json new file mode 100644 index 00000000000000..8c6982f9764a27 --- /dev/null +++ b/homeassistant/components/tibber/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tibber", + "name": "Tibber", + "documentation": "https://www.home-assistant.io/components/tibber", + "requirements": [ + "pyTibber==0.10.1" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen" + ] +} diff --git a/homeassistant/components/tikteck/manifest.json b/homeassistant/components/tikteck/manifest.json new file mode 100644 index 00000000000000..7edaf9ba978bff --- /dev/null +++ b/homeassistant/components/tikteck/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tikteck", + "name": "Tikteck", + "documentation": "https://www.home-assistant.io/components/tikteck", + "requirements": [ + "tikteck==0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tile/manifest.json b/homeassistant/components/tile/manifest.json new file mode 100644 index 00000000000000..3d26e8315ae4b6 --- /dev/null +++ b/homeassistant/components/tile/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tile", + "name": "Tile", + "documentation": "https://www.home-assistant.io/components/tile", + "requirements": [ + "pytile==2.0.6" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/time_date/manifest.json b/homeassistant/components/time_date/manifest.json new file mode 100644 index 00000000000000..bd620d4a18fc55 --- /dev/null +++ b/homeassistant/components/time_date/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "time_date", + "name": "Time date", + "documentation": "https://www.home-assistant.io/components/time_date", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/timer/manifest.json b/homeassistant/components/timer/manifest.json new file mode 100644 index 00000000000000..76a506faee86fb --- /dev/null +++ b/homeassistant/components/timer/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "timer", + "name": "Timer", + "documentation": "https://www.home-assistant.io/components/timer", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tod/manifest.json b/homeassistant/components/tod/manifest.json new file mode 100644 index 00000000000000..ff67748d64cd04 --- /dev/null +++ b/homeassistant/components/tod/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "tod", + "name": "Tod", + "documentation": "https://www.home-assistant.io/components/tod", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/todoist/manifest.json b/homeassistant/components/todoist/manifest.json new file mode 100644 index 00000000000000..7a6b4e2efab7a9 --- /dev/null +++ b/homeassistant/components/todoist/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "todoist", + "name": "Todoist", + "documentation": "https://www.home-assistant.io/components/todoist", + "requirements": [ + "todoist-python==7.0.17" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tof/manifest.json b/homeassistant/components/tof/manifest.json new file mode 100644 index 00000000000000..4e1857379c0349 --- /dev/null +++ b/homeassistant/components/tof/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tof", + "name": "Tof", + "documentation": "https://www.home-assistant.io/components/tof", + "requirements": [ + "VL53L1X2==0.1.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tomato/manifest.json b/homeassistant/components/tomato/manifest.json new file mode 100644 index 00000000000000..615ea9ecd7eaa7 --- /dev/null +++ b/homeassistant/components/tomato/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "tomato", + "name": "Tomato", + "documentation": "https://www.home-assistant.io/components/tomato", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/toon/manifest.json b/homeassistant/components/toon/manifest.json new file mode 100644 index 00000000000000..7dbf6768db68b6 --- /dev/null +++ b/homeassistant/components/toon/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "toon", + "name": "Toon", + "documentation": "https://www.home-assistant.io/components/toon", + "requirements": [ + "toonapilib==3.2.2" + ], + "dependencies": [], + "codeowners": [ + "@frenck" + ] +} diff --git a/homeassistant/components/torque/manifest.json b/homeassistant/components/torque/manifest.json new file mode 100644 index 00000000000000..3e69cb62e68010 --- /dev/null +++ b/homeassistant/components/torque/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "torque", + "name": "Torque", + "documentation": "https://www.home-assistant.io/components/torque", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/totalconnect/manifest.json b/homeassistant/components/totalconnect/manifest.json new file mode 100644 index 00000000000000..adb60599ae533c --- /dev/null +++ b/homeassistant/components/totalconnect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "totalconnect", + "name": "Totalconnect", + "documentation": "https://www.home-assistant.io/components/totalconnect", + "requirements": [ + "total_connect_client==0.25" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/touchline/manifest.json b/homeassistant/components/touchline/manifest.json new file mode 100644 index 00000000000000..5b8b4f521ee268 --- /dev/null +++ b/homeassistant/components/touchline/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "touchline", + "name": "Touchline", + "documentation": "https://www.home-assistant.io/components/touchline", + "requirements": [ + "pytouchline==0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tplink/manifest.json b/homeassistant/components/tplink/manifest.json new file mode 100644 index 00000000000000..c2a9ee2ee41351 --- /dev/null +++ b/homeassistant/components/tplink/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "tplink", + "name": "Tplink", + "documentation": "https://www.home-assistant.io/components/tplink", + "requirements": [ + "pyHS100==0.3.4", + "tplink==0.2.1" + ], + "dependencies": [], + "codeowners": [ + "@rytilahti" + ] +} diff --git a/homeassistant/components/tplink_lte/manifest.json b/homeassistant/components/tplink_lte/manifest.json new file mode 100644 index 00000000000000..e3efd8c83310a3 --- /dev/null +++ b/homeassistant/components/tplink_lte/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tplink_lte", + "name": "Tplink lte", + "documentation": "https://www.home-assistant.io/components/tplink_lte", + "requirements": [ + "tp-connected==0.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/traccar/manifest.json b/homeassistant/components/traccar/manifest.json new file mode 100644 index 00000000000000..57bd1383363b63 --- /dev/null +++ b/homeassistant/components/traccar/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "traccar", + "name": "Traccar", + "documentation": "https://www.home-assistant.io/components/traccar", + "requirements": [ + "pytraccar==0.5.0", + "stringcase==1.2.0" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/trackr/manifest.json b/homeassistant/components/trackr/manifest.json new file mode 100644 index 00000000000000..6ad348176ba11c --- /dev/null +++ b/homeassistant/components/trackr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "trackr", + "name": "Trackr", + "documentation": "https://www.home-assistant.io/components/trackr", + "requirements": [ + "pytrackr==0.0.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tradfri/manifest.json b/homeassistant/components/tradfri/manifest.json new file mode 100644 index 00000000000000..19e8348e987229 --- /dev/null +++ b/homeassistant/components/tradfri/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "tradfri", + "name": "Tradfri", + "documentation": "https://www.home-assistant.io/components/tradfri", + "requirements": [ + "pytradfri[async]==6.0.1" + ], + "dependencies": [], + "codeowners": [ + "@ggravlingen" + ] +} diff --git a/homeassistant/components/trafikverket_weatherstation/manifest.json b/homeassistant/components/trafikverket_weatherstation/manifest.json new file mode 100644 index 00000000000000..9bd734fe094065 --- /dev/null +++ b/homeassistant/components/trafikverket_weatherstation/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "trafikverket_weatherstation", + "name": "Trafikverket weatherstation", + "documentation": "https://www.home-assistant.io/components/trafikverket_weatherstation", + "requirements": [ + "pytrafikverket==0.1.5.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/transmission/manifest.json b/homeassistant/components/transmission/manifest.json new file mode 100644 index 00000000000000..bc5da64fcacd9b --- /dev/null +++ b/homeassistant/components/transmission/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "transmission", + "name": "Transmission", + "documentation": "https://www.home-assistant.io/components/transmission", + "requirements": [ + "transmissionrpc==0.11" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/transport_nsw/manifest.json b/homeassistant/components/transport_nsw/manifest.json new file mode 100644 index 00000000000000..491cce7407f861 --- /dev/null +++ b/homeassistant/components/transport_nsw/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "transport_nsw", + "name": "Transport nsw", + "documentation": "https://www.home-assistant.io/components/transport_nsw", + "requirements": [ + "PyTransportNSW==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/travisci/manifest.json b/homeassistant/components/travisci/manifest.json new file mode 100644 index 00000000000000..eb553fbe73c38f --- /dev/null +++ b/homeassistant/components/travisci/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "travisci", + "name": "Travisci", + "documentation": "https://www.home-assistant.io/components/travisci", + "requirements": [ + "TravisPy==0.3.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/trend/manifest.json b/homeassistant/components/trend/manifest.json new file mode 100644 index 00000000000000..865d7064db21a8 --- /dev/null +++ b/homeassistant/components/trend/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "trend", + "name": "Trend", + "documentation": "https://www.home-assistant.io/components/trend", + "requirements": [ + "numpy==1.16.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/tts/manifest.json b/homeassistant/components/tts/manifest.json new file mode 100644 index 00000000000000..ce600473cc5429 --- /dev/null +++ b/homeassistant/components/tts/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "tts", + "name": "Tts", + "documentation": "https://www.home-assistant.io/components/tts", + "requirements": [ + "mutagen==1.42.0" + ], + "dependencies": [ + "http" + ], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/tuya/manifest.json b/homeassistant/components/tuya/manifest.json new file mode 100644 index 00000000000000..f4361c89909c84 --- /dev/null +++ b/homeassistant/components/tuya/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "tuya", + "name": "Tuya", + "documentation": "https://www.home-assistant.io/components/tuya", + "requirements": [ + "tuyapy==0.1.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/twilio/manifest.json b/homeassistant/components/twilio/manifest.json new file mode 100644 index 00000000000000..dfb7dd4b14df4e --- /dev/null +++ b/homeassistant/components/twilio/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "twilio", + "name": "Twilio", + "documentation": "https://www.home-assistant.io/components/twilio", + "requirements": [ + "twilio==6.19.1" + ], + "dependencies": [ + "webhook" + ], + "codeowners": [] +} diff --git a/homeassistant/components/twilio_call/manifest.json b/homeassistant/components/twilio_call/manifest.json new file mode 100644 index 00000000000000..85545084c7b8c8 --- /dev/null +++ b/homeassistant/components/twilio_call/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "twilio_call", + "name": "Twilio call", + "documentation": "https://www.home-assistant.io/components/twilio_call", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/twilio_sms/manifest.json b/homeassistant/components/twilio_sms/manifest.json new file mode 100644 index 00000000000000..25cee38dbc8871 --- /dev/null +++ b/homeassistant/components/twilio_sms/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "twilio_sms", + "name": "Twilio sms", + "documentation": "https://www.home-assistant.io/components/twilio_sms", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/twitch/manifest.json b/homeassistant/components/twitch/manifest.json new file mode 100644 index 00000000000000..80bc795b536d09 --- /dev/null +++ b/homeassistant/components/twitch/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "twitch", + "name": "Twitch", + "documentation": "https://www.home-assistant.io/components/twitch", + "requirements": [ + "python-twitch-client==0.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/twitter/manifest.json b/homeassistant/components/twitter/manifest.json new file mode 100644 index 00000000000000..e721bb669ed41c --- /dev/null +++ b/homeassistant/components/twitter/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "twitter", + "name": "Twitter", + "documentation": "https://www.home-assistant.io/components/twitter", + "requirements": [ + "TwitterAPI==2.5.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ubee/manifest.json b/homeassistant/components/ubee/manifest.json new file mode 100644 index 00000000000000..c19c72e86862d3 --- /dev/null +++ b/homeassistant/components/ubee/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ubee", + "name": "Ubee", + "documentation": "https://www.home-assistant.io/components/ubee", + "requirements": [ + "pyubee==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/uber/manifest.json b/homeassistant/components/uber/manifest.json new file mode 100644 index 00000000000000..a7db237ab91444 --- /dev/null +++ b/homeassistant/components/uber/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "uber", + "name": "Uber", + "documentation": "https://www.home-assistant.io/components/uber", + "requirements": [ + "uber_rides==0.6.0" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/ubus/manifest.json b/homeassistant/components/ubus/manifest.json new file mode 100644 index 00000000000000..f886e84254b179 --- /dev/null +++ b/homeassistant/components/ubus/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ubus", + "name": "Ubus", + "documentation": "https://www.home-assistant.io/components/ubus", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ue_smart_radio/manifest.json b/homeassistant/components/ue_smart_radio/manifest.json new file mode 100644 index 00000000000000..189ac690758551 --- /dev/null +++ b/homeassistant/components/ue_smart_radio/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "ue_smart_radio", + "name": "Ue smart radio", + "documentation": "https://www.home-assistant.io/components/ue_smart_radio", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/uk_transport/manifest.json b/homeassistant/components/uk_transport/manifest.json new file mode 100644 index 00000000000000..be44a9d8cc82bb --- /dev/null +++ b/homeassistant/components/uk_transport/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "uk_transport", + "name": "Uk transport", + "documentation": "https://www.home-assistant.io/components/uk_transport", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json new file mode 100644 index 00000000000000..85a8453966346c --- /dev/null +++ b/homeassistant/components/unifi/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "unifi", + "name": "Unifi", + "documentation": "https://www.home-assistant.io/components/unifi", + "requirements": [ + "aiounifi==4", + "pyunifi==2.16" + ], + "dependencies": [], + "codeowners": [ + "@kane610" + ] +} diff --git a/homeassistant/components/unifi_direct/manifest.json b/homeassistant/components/unifi_direct/manifest.json new file mode 100644 index 00000000000000..515bd68d011f73 --- /dev/null +++ b/homeassistant/components/unifi_direct/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "unifi_direct", + "name": "Unifi direct", + "documentation": "https://www.home-assistant.io/components/unifi_direct", + "requirements": [ + "pexpect==4.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/universal/manifest.json b/homeassistant/components/universal/manifest.json new file mode 100644 index 00000000000000..ac72d10f07fbdd --- /dev/null +++ b/homeassistant/components/universal/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "universal", + "name": "Universal", + "documentation": "https://www.home-assistant.io/components/universal", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/upc_connect/manifest.json b/homeassistant/components/upc_connect/manifest.json new file mode 100644 index 00000000000000..926fb9acf887ed --- /dev/null +++ b/homeassistant/components/upc_connect/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "upc_connect", + "name": "Upc connect", + "documentation": "https://www.home-assistant.io/components/upc_connect", + "requirements": [ + "defusedxml==0.5.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/upcloud/manifest.json b/homeassistant/components/upcloud/manifest.json new file mode 100644 index 00000000000000..3a58d80f64aafe --- /dev/null +++ b/homeassistant/components/upcloud/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "upcloud", + "name": "Upcloud", + "documentation": "https://www.home-assistant.io/components/upcloud", + "requirements": [ + "upcloud-api==0.4.3" + ], + "dependencies": [], + "codeowners": [ + "@scop" + ] +} diff --git a/homeassistant/components/updater/manifest.json b/homeassistant/components/updater/manifest.json new file mode 100644 index 00000000000000..9275ef34968249 --- /dev/null +++ b/homeassistant/components/updater/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "updater", + "name": "Updater", + "documentation": "https://www.home-assistant.io/components/updater", + "requirements": [ + "distro==1.4.0" + ], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json new file mode 100644 index 00000000000000..75213ecc9b9495 --- /dev/null +++ b/homeassistant/components/upnp/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "upnp", + "name": "Upnp", + "documentation": "https://www.home-assistant.io/components/upnp", + "requirements": [ + "async-upnp-client==0.14.7" + ], + "dependencies": [], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/ups/manifest.json b/homeassistant/components/ups/manifest.json new file mode 100644 index 00000000000000..98db00c30948e1 --- /dev/null +++ b/homeassistant/components/ups/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ups", + "name": "Ups", + "documentation": "https://www.home-assistant.io/components/ups", + "requirements": [ + "upsmychoice==1.0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/uptime/manifest.json b/homeassistant/components/uptime/manifest.json new file mode 100644 index 00000000000000..1019717838108b --- /dev/null +++ b/homeassistant/components/uptime/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "uptime", + "name": "Uptime", + "documentation": "https://www.home-assistant.io/components/uptime", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/uptimerobot/manifest.json b/homeassistant/components/uptimerobot/manifest.json new file mode 100644 index 00000000000000..375baf12565e00 --- /dev/null +++ b/homeassistant/components/uptimerobot/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "uptimerobot", + "name": "Uptimerobot", + "documentation": "https://www.home-assistant.io/components/uptimerobot", + "requirements": [ + "pyuptimerobot==0.0.5" + ], + "dependencies": [], + "codeowners": [ + "@ludeeus" + ] +} diff --git a/homeassistant/components/uscis/manifest.json b/homeassistant/components/uscis/manifest.json new file mode 100644 index 00000000000000..f2ffcfbf8a379d --- /dev/null +++ b/homeassistant/components/uscis/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "uscis", + "name": "Uscis", + "documentation": "https://www.home-assistant.io/components/uscis", + "requirements": [ + "uscisstatus==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/usgs_earthquakes_feed/manifest.json b/homeassistant/components/usgs_earthquakes_feed/manifest.json new file mode 100644 index 00000000000000..0b3848dbde6f8f --- /dev/null +++ b/homeassistant/components/usgs_earthquakes_feed/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "usgs_earthquakes_feed", + "name": "Usgs earthquakes feed", + "documentation": "https://www.home-assistant.io/components/usgs_earthquakes_feed", + "requirements": [ + "geojson_client==0.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/usps/manifest.json b/homeassistant/components/usps/manifest.json new file mode 100644 index 00000000000000..9e2f8886d3acbd --- /dev/null +++ b/homeassistant/components/usps/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "usps", + "name": "Usps", + "documentation": "https://www.home-assistant.io/components/usps", + "requirements": [ + "myusps==1.3.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/utility_meter/manifest.json b/homeassistant/components/utility_meter/manifest.json new file mode 100644 index 00000000000000..59f4d1ca21b06b --- /dev/null +++ b/homeassistant/components/utility_meter/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "utility_meter", + "name": "Utility meter", + "documentation": "https://www.home-assistant.io/components/utility_meter", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@dgomes" + ] +} diff --git a/homeassistant/components/uvc/manifest.json b/homeassistant/components/uvc/manifest.json new file mode 100644 index 00000000000000..5c77f9ecc70bab --- /dev/null +++ b/homeassistant/components/uvc/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "uvc", + "name": "Uvc", + "documentation": "https://www.home-assistant.io/components/uvc", + "requirements": [ + "uvcclient==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/vacuum/manifest.json b/homeassistant/components/vacuum/manifest.json new file mode 100644 index 00000000000000..8dfbb8ed968c74 --- /dev/null +++ b/homeassistant/components/vacuum/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vacuum", + "name": "Vacuum", + "documentation": "https://www.home-assistant.io/components/vacuum", + "requirements": [], + "dependencies": [ + "group" + ], + "codeowners": [] +} diff --git a/homeassistant/components/vasttrafik/manifest.json b/homeassistant/components/vasttrafik/manifest.json new file mode 100644 index 00000000000000..47153dcf17f50d --- /dev/null +++ b/homeassistant/components/vasttrafik/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vasttrafik", + "name": "Vasttrafik", + "documentation": "https://www.home-assistant.io/components/vasttrafik", + "requirements": [ + "vtjp==0.1.14" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/velbus/manifest.json b/homeassistant/components/velbus/manifest.json new file mode 100644 index 00000000000000..4df9fe32dd983a --- /dev/null +++ b/homeassistant/components/velbus/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "velbus", + "name": "Velbus", + "documentation": "https://www.home-assistant.io/components/velbus", + "requirements": [ + "python-velbus==2.0.22" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/velux/manifest.json b/homeassistant/components/velux/manifest.json new file mode 100644 index 00000000000000..bac2587cdc9a87 --- /dev/null +++ b/homeassistant/components/velux/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "velux", + "name": "Velux", + "documentation": "https://www.home-assistant.io/components/velux", + "requirements": [ + "pyvlx==0.2.10" + ], + "dependencies": [], + "codeowners": [ + "@Julius2342" + ] +} diff --git a/homeassistant/components/venstar/manifest.json b/homeassistant/components/venstar/manifest.json new file mode 100644 index 00000000000000..e8b9158f72126e --- /dev/null +++ b/homeassistant/components/venstar/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "venstar", + "name": "Venstar", + "documentation": "https://www.home-assistant.io/components/venstar", + "requirements": [ + "venstarcolortouch==0.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/vera/manifest.json b/homeassistant/components/vera/manifest.json new file mode 100644 index 00000000000000..7b475c437c3cab --- /dev/null +++ b/homeassistant/components/vera/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vera", + "name": "Vera", + "documentation": "https://www.home-assistant.io/components/vera", + "requirements": [ + "pyvera==0.2.45" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/verisure/manifest.json b/homeassistant/components/verisure/manifest.json new file mode 100644 index 00000000000000..7c895233f770d0 --- /dev/null +++ b/homeassistant/components/verisure/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "verisure", + "name": "Verisure", + "documentation": "https://www.home-assistant.io/components/verisure", + "requirements": [ + "jsonpath==0.75", + "vsure==1.5.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/version/manifest.json b/homeassistant/components/version/manifest.json new file mode 100644 index 00000000000000..34f984f953a893 --- /dev/null +++ b/homeassistant/components/version/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "version", + "name": "Version", + "documentation": "https://www.home-assistant.io/components/version", + "requirements": [ + "pyhaversion==2.0.3" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json new file mode 100644 index 00000000000000..bba754d135fac8 --- /dev/null +++ b/homeassistant/components/vesync/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vesync", + "name": "Vesync", + "documentation": "https://www.home-assistant.io/components/vesync", + "requirements": [ + "pyvesync_v2==0.9.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/viaggiatreno/manifest.json b/homeassistant/components/viaggiatreno/manifest.json new file mode 100644 index 00000000000000..e145b26b0c9a42 --- /dev/null +++ b/homeassistant/components/viaggiatreno/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "viaggiatreno", + "name": "Viaggiatreno", + "documentation": "https://www.home-assistant.io/components/viaggiatreno", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/vizio/manifest.json b/homeassistant/components/vizio/manifest.json new file mode 100644 index 00000000000000..ac589de841ac24 --- /dev/null +++ b/homeassistant/components/vizio/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vizio", + "name": "Vizio", + "documentation": "https://www.home-assistant.io/components/vizio", + "requirements": [ + "pyvizio==0.0.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/vlc/manifest.json b/homeassistant/components/vlc/manifest.json new file mode 100644 index 00000000000000..a40b0e8c7d61d1 --- /dev/null +++ b/homeassistant/components/vlc/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vlc", + "name": "Vlc", + "documentation": "https://www.home-assistant.io/components/vlc", + "requirements": [ + "python-vlc==1.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/voicerss/manifest.json b/homeassistant/components/voicerss/manifest.json new file mode 100644 index 00000000000000..6f0b4ae5fd258d --- /dev/null +++ b/homeassistant/components/voicerss/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "voicerss", + "name": "Voicerss", + "documentation": "https://www.home-assistant.io/components/voicerss", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/volkszaehler/manifest.json b/homeassistant/components/volkszaehler/manifest.json new file mode 100644 index 00000000000000..db068e350566d9 --- /dev/null +++ b/homeassistant/components/volkszaehler/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "volkszaehler", + "name": "Volkszaehler", + "documentation": "https://www.home-assistant.io/components/volkszaehler", + "requirements": [ + "volkszaehler==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/volumio/manifest.json b/homeassistant/components/volumio/manifest.json new file mode 100644 index 00000000000000..e7c4bac4abd71b --- /dev/null +++ b/homeassistant/components/volumio/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "volumio", + "name": "Volumio", + "documentation": "https://www.home-assistant.io/components/volumio", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/volvooncall/manifest.json b/homeassistant/components/volvooncall/manifest.json new file mode 100644 index 00000000000000..aa691d7766c75e --- /dev/null +++ b/homeassistant/components/volvooncall/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "volvooncall", + "name": "Volvooncall", + "documentation": "https://www.home-assistant.io/components/volvooncall", + "requirements": [ + "volvooncall==0.8.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/vultr/manifest.json b/homeassistant/components/vultr/manifest.json new file mode 100644 index 00000000000000..5f5461f2d63fb7 --- /dev/null +++ b/homeassistant/components/vultr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "vultr", + "name": "Vultr", + "documentation": "https://www.home-assistant.io/components/vultr", + "requirements": [ + "vultr==0.1.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/w800rf32/manifest.json b/homeassistant/components/w800rf32/manifest.json new file mode 100644 index 00000000000000..920ee1120a7c50 --- /dev/null +++ b/homeassistant/components/w800rf32/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "w800rf32", + "name": "W800rf32", + "documentation": "https://www.home-assistant.io/components/w800rf32", + "requirements": [ + "pyW800rf32==0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wake_on_lan/manifest.json b/homeassistant/components/wake_on_lan/manifest.json new file mode 100644 index 00000000000000..dc689f8d617f56 --- /dev/null +++ b/homeassistant/components/wake_on_lan/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "wake_on_lan", + "name": "Wake on lan", + "documentation": "https://www.home-assistant.io/components/wake_on_lan", + "requirements": [ + "wakeonlan==1.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/waqi/manifest.json b/homeassistant/components/waqi/manifest.json new file mode 100644 index 00000000000000..4b692c669d1ea7 --- /dev/null +++ b/homeassistant/components/waqi/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "waqi", + "name": "Waqi", + "documentation": "https://www.home-assistant.io/components/waqi", + "requirements": [ + "waqiasync==1.0.0" + ], + "dependencies": [], + "codeowners": [ + "@andrey-git" + ] +} diff --git a/homeassistant/components/water_heater/manifest.json b/homeassistant/components/water_heater/manifest.json new file mode 100644 index 00000000000000..e291777483ef00 --- /dev/null +++ b/homeassistant/components/water_heater/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "water_heater", + "name": "Water heater", + "documentation": "https://www.home-assistant.io/components/water_heater", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/waterfurnace/manifest.json b/homeassistant/components/waterfurnace/manifest.json new file mode 100644 index 00000000000000..57aa663a348ebb --- /dev/null +++ b/homeassistant/components/waterfurnace/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "waterfurnace", + "name": "Waterfurnace", + "documentation": "https://www.home-assistant.io/components/waterfurnace", + "requirements": [ + "waterfurnace==1.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/watson_iot/manifest.json b/homeassistant/components/watson_iot/manifest.json new file mode 100644 index 00000000000000..8896f34f976afe --- /dev/null +++ b/homeassistant/components/watson_iot/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "watson_iot", + "name": "Watson iot", + "documentation": "https://www.home-assistant.io/components/watson_iot", + "requirements": [ + "ibmiotf==0.3.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/waze_travel_time/manifest.json b/homeassistant/components/waze_travel_time/manifest.json new file mode 100644 index 00000000000000..64b384356ce7ca --- /dev/null +++ b/homeassistant/components/waze_travel_time/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "waze_travel_time", + "name": "Waze travel time", + "documentation": "https://www.home-assistant.io/components/waze_travel_time", + "requirements": [ + "WazeRouteCalculator==0.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/weather/manifest.json b/homeassistant/components/weather/manifest.json new file mode 100644 index 00000000000000..7008c033f95bc9 --- /dev/null +++ b/homeassistant/components/weather/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "weather", + "name": "Weather", + "documentation": "https://www.home-assistant.io/components/weather", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/webhook/manifest.json b/homeassistant/components/webhook/manifest.json new file mode 100644 index 00000000000000..384e61aed2a848 --- /dev/null +++ b/homeassistant/components/webhook/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "webhook", + "name": "Webhook", + "documentation": "https://www.home-assistant.io/components/webhook", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [] +} diff --git a/homeassistant/components/weblink/manifest.json b/homeassistant/components/weblink/manifest.json new file mode 100644 index 00000000000000..7c30ad6c5d3549 --- /dev/null +++ b/homeassistant/components/weblink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "weblink", + "name": "Weblink", + "documentation": "https://www.home-assistant.io/components/weblink", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/webostv/manifest.json b/homeassistant/components/webostv/manifest.json new file mode 100644 index 00000000000000..0673c36e91f2a9 --- /dev/null +++ b/homeassistant/components/webostv/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "webostv", + "name": "Webostv", + "documentation": "https://www.home-assistant.io/components/webostv", + "requirements": [ + "pylgtv==0.1.9", + "websockets==6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/websocket_api/manifest.json b/homeassistant/components/websocket_api/manifest.json new file mode 100644 index 00000000000000..bc630b2947fca5 --- /dev/null +++ b/homeassistant/components/websocket_api/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "websocket_api", + "name": "Websocket api", + "documentation": "https://www.home-assistant.io/components/websocket_api", + "requirements": [], + "dependencies": [ + "http" + ], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/wemo/manifest.json b/homeassistant/components/wemo/manifest.json new file mode 100644 index 00000000000000..238be891886859 --- /dev/null +++ b/homeassistant/components/wemo/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "wemo", + "name": "Wemo", + "documentation": "https://www.home-assistant.io/components/wemo", + "requirements": [ + "pywemo==0.4.34" + ], + "dependencies": [], + "codeowners": [ + "@sqldiablo" + ] +} diff --git a/homeassistant/components/whois/manifest.json b/homeassistant/components/whois/manifest.json new file mode 100644 index 00000000000000..dec3e78a50362b --- /dev/null +++ b/homeassistant/components/whois/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "whois", + "name": "Whois", + "documentation": "https://www.home-assistant.io/components/whois", + "requirements": [ + "python-whois==0.7.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wink/manifest.json b/homeassistant/components/wink/manifest.json new file mode 100644 index 00000000000000..6ad6fa2b940966 --- /dev/null +++ b/homeassistant/components/wink/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "wink", + "name": "Wink", + "documentation": "https://www.home-assistant.io/components/wink", + "requirements": [ + "pubnubsub-handler==1.0.3", + "python-wink==1.10.3" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wirelesstag/manifest.json b/homeassistant/components/wirelesstag/manifest.json new file mode 100644 index 00000000000000..c3da00ce951c75 --- /dev/null +++ b/homeassistant/components/wirelesstag/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "wirelesstag", + "name": "Wirelesstag", + "documentation": "https://www.home-assistant.io/components/wirelesstag", + "requirements": [ + "wirelesstagpy==0.4.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json new file mode 100644 index 00000000000000..889ce4059bec6b --- /dev/null +++ b/homeassistant/components/workday/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "workday", + "name": "Workday", + "documentation": "https://www.home-assistant.io/components/workday", + "requirements": [ + "holidays==0.9.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/worldclock/manifest.json b/homeassistant/components/worldclock/manifest.json new file mode 100644 index 00000000000000..2da33f942b8f65 --- /dev/null +++ b/homeassistant/components/worldclock/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "worldclock", + "name": "Worldclock", + "documentation": "https://www.home-assistant.io/components/worldclock", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/worldtidesinfo/manifest.json b/homeassistant/components/worldtidesinfo/manifest.json new file mode 100644 index 00000000000000..dfc116c97db359 --- /dev/null +++ b/homeassistant/components/worldtidesinfo/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "worldtidesinfo", + "name": "Worldtidesinfo", + "documentation": "https://www.home-assistant.io/components/worldtidesinfo", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/worxlandroid/manifest.json b/homeassistant/components/worxlandroid/manifest.json new file mode 100644 index 00000000000000..3e7c626ddd0f8a --- /dev/null +++ b/homeassistant/components/worxlandroid/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "worxlandroid", + "name": "Worxlandroid", + "documentation": "https://www.home-assistant.io/components/worxlandroid", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wsdot/manifest.json b/homeassistant/components/wsdot/manifest.json new file mode 100644 index 00000000000000..c778ed1049f598 --- /dev/null +++ b/homeassistant/components/wsdot/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "wsdot", + "name": "Wsdot", + "documentation": "https://www.home-assistant.io/components/wsdot", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wunderground/manifest.json b/homeassistant/components/wunderground/manifest.json new file mode 100644 index 00000000000000..d14c9db419a53a --- /dev/null +++ b/homeassistant/components/wunderground/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "wunderground", + "name": "Wunderground", + "documentation": "https://www.home-assistant.io/components/wunderground", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/wunderlist/manifest.json b/homeassistant/components/wunderlist/manifest.json new file mode 100644 index 00000000000000..505447f454c031 --- /dev/null +++ b/homeassistant/components/wunderlist/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "wunderlist", + "name": "Wunderlist", + "documentation": "https://www.home-assistant.io/components/wunderlist", + "requirements": [ + "wunderpy2==0.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/x10/manifest.json b/homeassistant/components/x10/manifest.json new file mode 100644 index 00000000000000..2fbe16a6e7adae --- /dev/null +++ b/homeassistant/components/x10/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "x10", + "name": "X10", + "documentation": "https://www.home-assistant.io/components/x10", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/xbox_live/manifest.json b/homeassistant/components/xbox_live/manifest.json new file mode 100644 index 00000000000000..0d80ce770ced7c --- /dev/null +++ b/homeassistant/components/xbox_live/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "xbox_live", + "name": "Xbox live", + "documentation": "https://www.home-assistant.io/components/xbox_live", + "requirements": [ + "xboxapi==0.1.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/xeoma/manifest.json b/homeassistant/components/xeoma/manifest.json new file mode 100644 index 00000000000000..ee8ed2f6de31de --- /dev/null +++ b/homeassistant/components/xeoma/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "xeoma", + "name": "Xeoma", + "documentation": "https://www.home-assistant.io/components/xeoma", + "requirements": [ + "pyxeoma==1.4.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/xfinity/manifest.json b/homeassistant/components/xfinity/manifest.json new file mode 100644 index 00000000000000..71750ccf0889a4 --- /dev/null +++ b/homeassistant/components/xfinity/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "xfinity", + "name": "Xfinity", + "documentation": "https://www.home-assistant.io/components/xfinity", + "requirements": [ + "xfinity-gateway==0.0.4" + ], + "dependencies": [], + "codeowners": [ + "@cisasteelersfan" + ] +} diff --git a/homeassistant/components/xiaomi/manifest.json b/homeassistant/components/xiaomi/manifest.json new file mode 100644 index 00000000000000..158a2e9b2fc363 --- /dev/null +++ b/homeassistant/components/xiaomi/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "xiaomi", + "name": "Xiaomi", + "documentation": "https://www.home-assistant.io/components/xiaomi", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/xiaomi_aqara/manifest.json b/homeassistant/components/xiaomi_aqara/manifest.json new file mode 100644 index 00000000000000..a79f29604972e1 --- /dev/null +++ b/homeassistant/components/xiaomi_aqara/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "xiaomi_aqara", + "name": "Xiaomi aqara", + "documentation": "https://www.home-assistant.io/components/xiaomi_aqara", + "requirements": [ + "PyXiaomiGateway==0.12.2" + ], + "dependencies": [], + "codeowners": [ + "@danielhiversen", + "@syssi" + ] +} diff --git a/homeassistant/components/xiaomi_miio/manifest.json b/homeassistant/components/xiaomi_miio/manifest.json new file mode 100644 index 00000000000000..d7e0d0d732eee8 --- /dev/null +++ b/homeassistant/components/xiaomi_miio/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "xiaomi_miio", + "name": "Xiaomi miio", + "documentation": "https://www.home-assistant.io/components/xiaomi_miio", + "requirements": [ + "construct==2.9.45", + "python-miio==0.4.5" + ], + "dependencies": [], + "codeowners": [ + "@rytilahti", + "@syssi" + ] +} diff --git a/homeassistant/components/xiaomi_tv/manifest.json b/homeassistant/components/xiaomi_tv/manifest.json new file mode 100644 index 00000000000000..221532c1c8d1f6 --- /dev/null +++ b/homeassistant/components/xiaomi_tv/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "xiaomi_tv", + "name": "Xiaomi tv", + "documentation": "https://www.home-assistant.io/components/xiaomi_tv", + "requirements": [ + "pymitv==1.4.3" + ], + "dependencies": [], + "codeowners": [ + "@fattdev" + ] +} diff --git a/homeassistant/components/xmpp/manifest.json b/homeassistant/components/xmpp/manifest.json new file mode 100644 index 00000000000000..d8e4e5c4da6142 --- /dev/null +++ b/homeassistant/components/xmpp/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "xmpp", + "name": "Xmpp", + "documentation": "https://www.home-assistant.io/components/xmpp", + "requirements": [ + "slixmpp==1.4.2" + ], + "dependencies": [], + "codeowners": [ + "@fabaff" + ] +} diff --git a/homeassistant/components/xs1/manifest.json b/homeassistant/components/xs1/manifest.json new file mode 100644 index 00000000000000..4ee13acf6472e7 --- /dev/null +++ b/homeassistant/components/xs1/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "xs1", + "name": "Xs1", + "documentation": "https://www.home-assistant.io/components/xs1", + "requirements": [ + "xs1-api-client==2.3.5" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/yale_smart_alarm/manifest.json b/homeassistant/components/yale_smart_alarm/manifest.json new file mode 100644 index 00000000000000..7b786c7bf7c58e --- /dev/null +++ b/homeassistant/components/yale_smart_alarm/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "yale_smart_alarm", + "name": "Yale smart alarm", + "documentation": "https://www.home-assistant.io/components/yale_smart_alarm", + "requirements": [ + "yalesmartalarmclient==0.1.6" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/yamaha/manifest.json b/homeassistant/components/yamaha/manifest.json new file mode 100644 index 00000000000000..5a277fc7ce879c --- /dev/null +++ b/homeassistant/components/yamaha/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "yamaha", + "name": "Yamaha", + "documentation": "https://www.home-assistant.io/components/yamaha", + "requirements": [ + "rxv==0.6.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/yamaha_musiccast/manifest.json b/homeassistant/components/yamaha_musiccast/manifest.json new file mode 100644 index 00000000000000..7769026e09279a --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "yamaha_musiccast", + "name": "Yamaha musiccast", + "documentation": "https://www.home-assistant.io/components/yamaha_musiccast", + "requirements": [ + "pymusiccast==0.1.6" + ], + "dependencies": [], + "codeowners": [ + "@jalmeroth" + ] +} diff --git a/homeassistant/components/yandextts/manifest.json b/homeassistant/components/yandextts/manifest.json new file mode 100644 index 00000000000000..7f622a1e25fe68 --- /dev/null +++ b/homeassistant/components/yandextts/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "yandextts", + "name": "Yandextts", + "documentation": "https://www.home-assistant.io/components/yandextts", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json new file mode 100644 index 00000000000000..f734f092a1a050 --- /dev/null +++ b/homeassistant/components/yeelight/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "yeelight", + "name": "Yeelight", + "documentation": "https://www.home-assistant.io/components/yeelight", + "requirements": [ + "yeelight==0.4.4" + ], + "dependencies": [], + "codeowners": [ + "@rytilahti", + "@zewelor" + ] +} diff --git a/homeassistant/components/yeelightsunflower/manifest.json b/homeassistant/components/yeelightsunflower/manifest.json new file mode 100644 index 00000000000000..1a75472b80131a --- /dev/null +++ b/homeassistant/components/yeelightsunflower/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "yeelightsunflower", + "name": "Yeelightsunflower", + "documentation": "https://www.home-assistant.io/components/yeelightsunflower", + "requirements": [ + "yeelightsunflower==0.0.10" + ], + "dependencies": [], + "codeowners": [ + "@lindsaymarkward" + ] +} diff --git a/homeassistant/components/yessssms/manifest.json b/homeassistant/components/yessssms/manifest.json new file mode 100644 index 00000000000000..103a9fce31ede2 --- /dev/null +++ b/homeassistant/components/yessssms/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "yessssms", + "name": "Yessssms", + "documentation": "https://www.home-assistant.io/components/yessssms", + "requirements": [ + "YesssSMS==0.2.3" + ], + "dependencies": [], + "codeowners": [ + "@flowolf" + ] +} diff --git a/homeassistant/components/yi/manifest.json b/homeassistant/components/yi/manifest.json new file mode 100644 index 00000000000000..0a1a6aabc576db --- /dev/null +++ b/homeassistant/components/yi/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "yi", + "name": "Yi", + "documentation": "https://www.home-assistant.io/components/yi", + "requirements": [ + "aioftp==0.12.0" + ], + "dependencies": [], + "codeowners": [ + "@bachya" + ] +} diff --git a/homeassistant/components/yr/manifest.json b/homeassistant/components/yr/manifest.json new file mode 100644 index 00000000000000..ec12f6cdac4417 --- /dev/null +++ b/homeassistant/components/yr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "yr", + "name": "Yr", + "documentation": "https://www.home-assistant.io/components/yr", + "requirements": [ + "xmltodict==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/yweather/manifest.json b/homeassistant/components/yweather/manifest.json new file mode 100644 index 00000000000000..c3048601595f85 --- /dev/null +++ b/homeassistant/components/yweather/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "yweather", + "name": "Yweather", + "documentation": "https://www.home-assistant.io/components/yweather", + "requirements": [ + "yahooweather==0.10" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zabbix/manifest.json b/homeassistant/components/zabbix/manifest.json new file mode 100644 index 00000000000000..c0f100fa62ffa7 --- /dev/null +++ b/homeassistant/components/zabbix/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zabbix", + "name": "Zabbix", + "documentation": "https://www.home-assistant.io/components/zabbix", + "requirements": [ + "pyzabbix==0.7.4" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zamg/manifest.json b/homeassistant/components/zamg/manifest.json new file mode 100644 index 00000000000000..ce16e1b523c392 --- /dev/null +++ b/homeassistant/components/zamg/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "zamg", + "name": "Zamg", + "documentation": "https://www.home-assistant.io/components/zamg", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zengge/manifest.json b/homeassistant/components/zengge/manifest.json new file mode 100644 index 00000000000000..b846c95f5fa57c --- /dev/null +++ b/homeassistant/components/zengge/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zengge", + "name": "Zengge", + "documentation": "https://www.home-assistant.io/components/zengge", + "requirements": [ + "zengge==0.2" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json new file mode 100644 index 00000000000000..bd7cf3ec0d620a --- /dev/null +++ b/homeassistant/components/zeroconf/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "zeroconf", + "name": "Zeroconf", + "documentation": "https://www.home-assistant.io/components/zeroconf", + "requirements": [ + "zeroconf==0.21.3" + ], + "dependencies": [ + "api" + ], + "codeowners": [ + "@robbiet480" + ] +} diff --git a/homeassistant/components/zestimate/manifest.json b/homeassistant/components/zestimate/manifest.json new file mode 100644 index 00000000000000..1d67ddbd5817cc --- /dev/null +++ b/homeassistant/components/zestimate/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zestimate", + "name": "Zestimate", + "documentation": "https://www.home-assistant.io/components/zestimate", + "requirements": [ + "xmltodict==0.11.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json new file mode 100644 index 00000000000000..e2b2c54fd93155 --- /dev/null +++ b/homeassistant/components/zha/manifest.json @@ -0,0 +1,17 @@ +{ + "domain": "zha", + "name": "Zigbee Home Automation", + "documentation": "https://www.home-assistant.io/components/zha", + "requirements": [ + "bellows-homeassistant==0.7.2", + "zha-quirks==0.0.7", + "zigpy-deconz==0.1.3", + "zigpy-homeassistant==0.3.1", + "zigpy-xbee-homeassistant==0.1.3" + ], + "dependencies": [], + "codeowners": [ + "@dmulcahey", + "@adminiuga" + ] +} diff --git a/homeassistant/components/zhong_hong/manifest.json b/homeassistant/components/zhong_hong/manifest.json new file mode 100644 index 00000000000000..6382a830dcfdb4 --- /dev/null +++ b/homeassistant/components/zhong_hong/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zhong_hong", + "name": "Zhong hong", + "documentation": "https://www.home-assistant.io/components/zhong_hong", + "requirements": [ + "zhong_hong_hvac==1.0.9" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zigbee/manifest.json b/homeassistant/components/zigbee/manifest.json new file mode 100644 index 00000000000000..1e4076b84392c5 --- /dev/null +++ b/homeassistant/components/zigbee/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zigbee", + "name": "Zigbee", + "documentation": "https://www.home-assistant.io/components/zigbee", + "requirements": [ + "xbee-helper==0.0.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/ziggo_mediabox_xl/manifest.json b/homeassistant/components/ziggo_mediabox_xl/manifest.json new file mode 100644 index 00000000000000..9e587137922e70 --- /dev/null +++ b/homeassistant/components/ziggo_mediabox_xl/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ziggo_mediabox_xl", + "name": "Ziggo mediabox xl", + "documentation": "https://www.home-assistant.io/components/ziggo_mediabox_xl", + "requirements": [ + "ziggo-mediabox-xl==1.1.0" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/zone/manifest.json b/homeassistant/components/zone/manifest.json new file mode 100644 index 00000000000000..897908b61daa8a --- /dev/null +++ b/homeassistant/components/zone/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "zone", + "name": "Zone", + "documentation": "https://www.home-assistant.io/components/zone", + "requirements": [], + "dependencies": [], + "codeowners": [ + "@home-assistant/core" + ] +} diff --git a/homeassistant/components/zoneminder/manifest.json b/homeassistant/components/zoneminder/manifest.json new file mode 100644 index 00000000000000..9d371fbabf7670 --- /dev/null +++ b/homeassistant/components/zoneminder/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "zoneminder", + "name": "Zoneminder", + "documentation": "https://www.home-assistant.io/components/zoneminder", + "requirements": [ + "zm-py==0.3.3" + ], + "dependencies": [], + "codeowners": [ + "@rohankapoorcom" + ] +} diff --git a/homeassistant/components/zwave/manifest.json b/homeassistant/components/zwave/manifest.json new file mode 100644 index 00000000000000..ac7e327f19aa40 --- /dev/null +++ b/homeassistant/components/zwave/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "zwave", + "name": "Z-Wave", + "documentation": "https://www.home-assistant.io/components/zwave", + "requirements": [ + "homeassistant-pyozw==0.1.3", + "pydispatcher==2.0.5" + ], + "dependencies": [], + "codeowners": [ + "@home-assistant/z-wave" + ] +} From d81a627739b8c2fc97cbb36540ec63e2a256b0ac Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 3 Apr 2019 21:44:15 -0700 Subject: [PATCH 397/605] Add a .codecov.yml to control coverage statuses and enable notifications (#22707) * Add a .codecov.yml to control coverage statuses and enable notifications * Comment about Slack/Discord notification --- .codecov.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000000000..96a39e7319bee2 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,13 @@ +codecov: + branch: dev +coverage: + status: + project: + default: + target: 90 + threshold: 0.09 + notify: + # Notify codecov room in Discord. The webhook URL (encrypted below) ends in /slack which is why we configure a Slack notification. + slack: + default: + url: "secret:TgWDUM4Jw0w7wMJxuxNF/yhSOHglIo1fGwInJnRLEVPy2P2aLimkoK1mtKCowH5TFw+baUXVXT3eAqefbdvIuM8BjRR4aRji95C6CYyD0QHy4N8i7nn1SQkWDPpS8IthYTg07rUDF7s5guurkKv2RrgoCdnnqjAMSzHoExMOF7xUmblMdhBTWJgBpWEhASJy85w/xxjlsE1xoTkzeJu9Q67pTXtRcn+5kb5/vIzPSYg=" From a5a926bcc692027af8e5a4e9bb2812ab7665d864 Mon Sep 17 00:00:00 2001 From: Kyle Niewiada Date: Thu, 4 Apr 2019 00:51:01 -0400 Subject: [PATCH 398/605] Raise ConfigEntryNotReady for MQTT connection exception (#22540) * Raise ConfigEntryNotReady for connection exception Raise ConfigEntryNotReady for the connection exception like if the MQTT Server container/device is being restarted or was unavailable on boot. * Add new exception * grammar fix * Possibly resolve hound comments * raise `ConfigEntryNotReady` for mqtt connection error * revert exceptions.py * Update exceptions.py * modify test to handle exception * use constants to control exception scope * Raise ConfigEntryNotReady for connection exception Raise ConfigEntryNotReady for the connection exception like if the MQTT Server container/device is being restarted or was unavailable on boot. * Add new exception * Add new exception * grammar fix * Possibly resolve hound comments * raise `ConfigEntryNotReady` for mqtt connection error * revert exceptions.py * Update exceptions.py * modify test to handle exception * use constants to control exception scope * revert test change as it's not the same thing * Update test_init.py * Add test for MQTT OSError * revert file changes from a bad rebase * Rewrite test with valid syntax * rewrite test to be less ambiguous * add empty line * add back 'axis' * Remove empty line * Update tests and undo merge from earlier * correctly restore test for no connect broker * fix test mock correctly * line was too long. hit enter. --- homeassistant/components/mqtt/__init__.py | 22 +++++++++++++++------- tests/components/mqtt/test_init.py | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 3f1f8617689c58..81d2dd8ea032c9 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -23,7 +23,8 @@ CONF_PROTOCOL, CONF_USERNAME, CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import Event, ServiceCall, callback -from homeassistant.exceptions import HomeAssistantError, Unauthorized +from homeassistant.exceptions import ( + HomeAssistantError, Unauthorized, ConfigEntryNotReady) from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ( @@ -104,6 +105,10 @@ MAX_RECONNECT_WAIT = 300 # seconds +CONNECTION_SUCCESS = 'connection_success' +CONNECTION_FAILED = 'connection_failed' +CONNECTION_FAILED_RECOVERABLE = 'connection_failed_recoverable' + def valid_topic(value: Any) -> str: """Validate that this is a valid topic name/filter.""" @@ -569,11 +574,14 @@ async def async_setup_entry(hass, entry): tls_version=tls_version, ) - success = await hass.data[DATA_MQTT].async_connect() # type: bool + result = await hass.data[DATA_MQTT].async_connect() # type: str - if not success: + if result == CONNECTION_FAILED: return False + if result == CONNECTION_FAILED_RECOVERABLE: + raise ConfigEntryNotReady + async def async_stop_mqtt(event: Event): """Stop MQTT component.""" await hass.data[DATA_MQTT].async_disconnect() @@ -685,7 +693,7 @@ async def async_publish(self, topic: str, payload: PublishPayloadType, await self.hass.async_add_job( self._mqttc.publish, topic, payload, qos, retain) - async def async_connect(self) -> bool: + async def async_connect(self) -> str: """Connect to the host. Does process messages yet. This method is a coroutine. @@ -696,15 +704,15 @@ async def async_connect(self) -> bool: self._mqttc.connect, self.broker, self.port, self.keepalive) except OSError as err: _LOGGER.error("Failed to connect due to exception: %s", err) - return False + return CONNECTION_FAILED_RECOVERABLE if result != 0: import paho.mqtt.client as mqtt _LOGGER.error("Failed to connect: %s", mqtt.error_string(result)) - return False + return CONNECTION_FAILED self._mqttc.loop_start() - return True + return CONNECTION_SUCCESS @callback def async_disconnect(self): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 5c441a68bea3e8..144ee9c43d8017 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -12,6 +12,7 @@ ATTR_DOMAIN, ATTR_SERVICE, EVENT_CALL_SERVICE, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback from homeassistant.setup import async_setup_component +from homeassistant.exceptions import ConfigEntryNotReady from tests.common import ( MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component, @@ -621,6 +622,19 @@ async def test_setup_fails_if_no_connect_broker(hass): assert not await mqtt.async_setup_entry(hass, entry) +async def test_setup_raises_ConfigEntryNotReady_if_no_connect_broker(hass): + """Test for setup failure if connection to broker is missing.""" + entry = MockConfigEntry(domain=mqtt.DOMAIN, data={ + mqtt.CONF_BROKER: 'test-broker' + }) + + with mock.patch('paho.mqtt.client.Client') as mock_client: + mock_client().connect = mock.Mock( + side_effect=OSError("Connection error")) + with pytest.raises(ConfigEntryNotReady): + await mqtt.async_setup_entry(hass, entry) + + async def test_setup_uses_certificate_on_certificate_set_to_auto( hass, mock_MQTT): """Test setup uses bundled certs when certificate is set to auto.""" From 8e39939b7ec924296e42f6554bbb1fb5ae4716c5 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Thu, 4 Apr 2019 06:52:23 +0200 Subject: [PATCH 399/605] Add device_class_power to sensor (#22691) * Add device_class_power to sensor * Fix comment --- homeassistant/components/homematicip_cloud/sensor.py | 9 +++++++-- homeassistant/components/sensor/__init__.py | 10 ++++++---- homeassistant/const.py | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index e053c191c6baae..2038433df4fa80 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -2,8 +2,8 @@ import logging from homeassistant.const import ( - DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, - POWER_WATT, TEMP_CELSIUS) + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_POWER, + DEVICE_CLASS_TEMPERATURE, POWER_WATT, TEMP_CELSIUS) from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice @@ -238,6 +238,11 @@ def __init__(self, home, device): """Initialize the device.""" super().__init__(home, device, 'Power') + @property + def device_class(self): + """Return the device class of the sensor.""" + return DEVICE_CLASS_POWER + @property def state(self): """Represenation of the HomematicIP power comsumption value.""" diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 031657066cbdd5..e11ace9749c996 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -5,12 +5,13 @@ import voluptuous as vol -from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.config_validation import ( # noqa - PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE) from homeassistant.const import ( DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_PRESSURE) + DEVICE_CLASS_POWER, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TIMESTAMP) +from homeassistant.helpers.config_validation import ( # noqa + PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE) +from homeassistant.helpers.entity_component import EntityComponent _LOGGER = logging.getLogger(__name__) @@ -26,6 +27,7 @@ DEVICE_CLASS_TEMPERATURE, # temperature (C/F) DEVICE_CLASS_TIMESTAMP, # timestamp (ISO8601) DEVICE_CLASS_PRESSURE, # pressure (hPa/mbar) + DEVICE_CLASS_POWER, # power (W/kW) ] DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9987e0f8e99604..e0130f3ccefadf 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -188,6 +188,7 @@ DEVICE_CLASS_TEMPERATURE = 'temperature' DEVICE_CLASS_TIMESTAMP = 'timestamp' DEVICE_CLASS_PRESSURE = 'pressure' +DEVICE_CLASS_POWER = 'power' # #### STATES #### STATE_ON = 'on' From afac09932ff9b1a931b4958ebc7266a52e542ef4 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Wed, 3 Apr 2019 23:31:55 -0700 Subject: [PATCH 400/605] Remove all config deprecations invalidated in 0.91 (#22704) * Remove all config deprecations invalidated in 0.91 * Fix lint --- homeassistant/components/broadlink/sensor.py | 33 ++++------ homeassistant/components/darksky/sensor.py | 65 ++++++++----------- .../components/fastdotcom/__init__.py | 23 ++----- homeassistant/components/fedex/sensor.py | 30 +++------ homeassistant/components/freedns/__init__.py | 30 +++------ .../components/mythicbeastsdns/__init__.py | 31 +++------ .../components/speedtestdotnet/__init__.py | 37 ++++------- .../components/tellduslive/__init__.py | 31 +++------ .../components/tplink_lte/__init__.py | 26 ++------ homeassistant/components/ups/sensor.py | 35 ++++------ .../components/volvooncall/__init__.py | 57 +++++++--------- homeassistant/const.py | 5 -- 12 files changed, 138 insertions(+), 265 deletions(-) diff --git a/homeassistant/components/broadlink/sensor.py b/homeassistant/components/broadlink/sensor.py index 60f1ed5c6bc858..b3ce245a979ae7 100644 --- a/homeassistant/components/broadlink/sensor.py +++ b/homeassistant/components/broadlink/sensor.py @@ -1,19 +1,18 @@ """Support for the Broadlink RM2 Pro (only temperature) and A1 devices.""" -from datetime import timedelta import binascii import logging import socket +from datetime import timedelta import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_HOST, CONF_MAC, CONF_MONITORED_CONDITIONS, CONF_NAME, TEMP_CELSIUS, - CONF_TIMEOUT, CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) + CONF_TIMEOUT, CONF_SCAN_INTERVAL) from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['broadlink==0.9.0'] @@ -31,24 +30,14 @@ 'noise': ['Noise', ' '], } -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEVICE_DEFAULT_NAME): vol.Coerce(str), - vol.Optional(CONF_MONITORED_CONDITIONS, default=[]): - vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_MAC): cv.string, - vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=SCAN_INTERVAL - ) -) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEVICE_DEFAULT_NAME): vol.Coerce(str), + vol.Optional(CONF_MONITORED_CONDITIONS, default=[]): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_MAC): cv.string, + vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int +}) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index 70b07ee773f65c..0e87593b25ca66 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -1,17 +1,16 @@ """Support for Dark Sky weather service.""" -from datetime import timedelta import logging +from datetime import timedelta +import voluptuous as vol from requests.exceptions import ( ConnectionError as ConnectError, HTTPError, Timeout) -import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, - CONF_MONITORED_CONDITIONS, CONF_NAME, UNIT_UV_INDEX, CONF_UPDATE_INTERVAL, - CONF_SCAN_INTERVAL, CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) -import homeassistant.helpers.config_validation as cv + CONF_MONITORED_CONDITIONS, CONF_NAME, UNIT_UV_INDEX, CONF_SCAN_INTERVAL) from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle @@ -166,39 +165,29 @@ ALLOWED_UNITS = ['auto', 'si', 'us', 'ca', 'uk', 'uk2'] -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA.extend({ - vol.Required(CONF_MONITORED_CONDITIONS): - vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), - vol.Required(CONF_API_KEY): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNITS): vol.In(ALLOWED_UNITS), - vol.Optional(CONF_LANGUAGE, - default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES), - vol.Inclusive( - CONF_LATITUDE, - 'coordinates', - 'Latitude and longitude must exist together' - ): cv.latitude, - vol.Inclusive( - CONF_LONGITUDE, - 'coordinates', - 'Latitude and longitude must exist together' - ): cv.longitude, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_FORECAST): - vol.All(cv.ensure_list, [vol.Range(min=0, max=7)]), - vol.Optional(CONF_HOURLY_FORECAST): - vol.All(cv.ensure_list, [vol.Range(min=0, max=48)]), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=SCAN_INTERVAL - ) -) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_MONITORED_CONDITIONS): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), + vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNITS): vol.In(ALLOWED_UNITS), + vol.Optional(CONF_LANGUAGE, + default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES), + vol.Inclusive( + CONF_LATITUDE, + 'coordinates', + 'Latitude and longitude must exist together' + ): cv.latitude, + vol.Inclusive( + CONF_LONGITUDE, + 'coordinates', + 'Latitude and longitude must exist together' + ): cv.longitude, + vol.Optional(CONF_FORECAST): + vol.All(cv.ensure_list, [vol.Range(min=0, max=7)]), + vol.Optional(CONF_HOURLY_FORECAST): + vol.All(cv.ensure_list, [vol.Range(min=0, max=48)]), +}) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py index 2e092e527c571c..973cc8e36597f1 100644 --- a/homeassistant/components/fastdotcom/__init__.py +++ b/homeassistant/components/fastdotcom/__init__.py @@ -5,8 +5,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.const import CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, \ - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION +from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval @@ -23,21 +22,11 @@ DEFAULT_INTERVAL = timedelta(hours=1) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_MANUAL, default=False): cv.boolean, - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=DEFAULT_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): + vol.All(cv.time_period, cv.positive_timedelta), + vol.Optional(CONF_MANUAL, default=False): cv.boolean, + }) }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/fedex/sensor.py b/homeassistant/components/fedex/sensor.py index f535195bd075ac..74ad4f7d0e53c4 100644 --- a/homeassistant/components/fedex/sensor.py +++ b/homeassistant/components/fedex/sensor.py @@ -1,20 +1,18 @@ """Sensor for Fedex packages.""" -from collections import defaultdict import logging +from collections import defaultdict from datetime import timedelta import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (CONF_NAME, CONF_USERNAME, CONF_PASSWORD, - ATTR_ATTRIBUTION, CONF_UPDATE_INTERVAL, - CONF_SCAN_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) + ATTR_ATTRIBUTION, CONF_SCAN_INTERVAL) from homeassistant.helpers.entity import Entity -from homeassistant.util import slugify from homeassistant.util import Throttle +from homeassistant.util import slugify from homeassistant.util.dt import now, parse_date -import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['fedexdeliverymanager==1.0.6'] @@ -30,21 +28,11 @@ SCAN_INTERVAL = timedelta(seconds=1800) -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=SCAN_INTERVAL - ) -) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/freedns/__init__.py b/homeassistant/components/freedns/__init__.py index a0b1475774565b..1986c932e22cb1 100644 --- a/homeassistant/components/freedns/__init__.py +++ b/homeassistant/components/freedns/__init__.py @@ -1,16 +1,16 @@ """Integrate with FreeDNS Dynamic DNS service at freedns.afraid.org.""" import asyncio -from datetime import timedelta import logging +from datetime import timedelta import aiohttp import async_timeout import voluptuous as vol -from homeassistant.const import (CONF_URL, CONF_ACCESS_TOKEN, - CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_ACCESS_TOKEN, CONF_SCAN_INTERVAL, CONF_URL +) _LOGGER = logging.getLogger(__name__) @@ -22,22 +22,12 @@ UPDATE_URL = 'https://freedns.afraid.org/dynamic/update.php' CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Exclusive(CONF_URL, DOMAIN): cv.string, - vol.Exclusive(CONF_ACCESS_TOKEN, DOMAIN): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=DEFAULT_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Exclusive(CONF_URL, DOMAIN): cv.string, + vol.Exclusive(CONF_ACCESS_TOKEN, DOMAIN): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): + vol.All(cv.time_period, cv.positive_timedelta), + }), }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/mythicbeastsdns/__init__.py b/homeassistant/components/mythicbeastsdns/__init__.py index 3d0d250557bf4f..4db53bf0407cf4 100644 --- a/homeassistant/components/mythicbeastsdns/__init__.py +++ b/homeassistant/components/mythicbeastsdns/__init__.py @@ -1,15 +1,14 @@ """Support for Mythic Beasts Dynamic DNS service.""" -from datetime import timedelta import logging +from datetime import timedelta import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.const import ( - CONF_HOST, CONF_DOMAIN, CONF_PASSWORD, CONF_UPDATE_INTERVAL, - CONF_SCAN_INTERVAL, CONF_UPDATE_INTERVAL_INVALIDATION_VERSION + CONF_DOMAIN, CONF_HOST, CONF_PASSWORD, CONF_SCAN_INTERVAL ) from homeassistant.helpers.aiohttp_client import async_get_clientsession -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_time_interval REQUIREMENTS = ['mbddns==0.1.2'] @@ -21,23 +20,13 @@ DEFAULT_INTERVAL = timedelta(minutes=10) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Required(CONF_DOMAIN): cv.string, - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=DEFAULT_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Required(CONF_DOMAIN): cv.string, + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): + vol.All(cv.time_period, cv.positive_timedelta), + }) }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index f140f881ef4c7f..48953874e8c528 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -1,18 +1,17 @@ """Support for testing internet speed via Speedtest.net.""" -from datetime import timedelta import logging +from datetime import timedelta import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( - CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL, CONF_UPDATE_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) -import homeassistant.helpers.config_validation as cv + CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL +) from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval - from .const import DATA_UPDATED, DOMAIN, SENSOR_TYPES REQUIREMENTS = ['speedtest-cli==2.1.1'] @@ -25,25 +24,15 @@ DEFAULT_INTERVAL = timedelta(hours=1) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Optional(CONF_SERVER_ID): cv.positive_int, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): - vol.All(cv.time_period, cv.positive_timedelta), - vol.Optional(CONF_MANUAL, default=False): cv.boolean, - vol.Optional( - CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES) - ): vol.All(cv.ensure_list, [vol.In(list(SENSOR_TYPES))]) - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=DEFAULT_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Optional(CONF_SERVER_ID): cv.positive_int, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL): + vol.All(cv.time_period, cv.positive_timedelta), + vol.Optional(CONF_MANUAL, default=False): cv.boolean, + vol.Optional( + CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES) + ): vol.All(cv.ensure_list, [vol.In(list(SENSOR_TYPES))]) + }) }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/tellduslive/__init__.py b/homeassistant/components/tellduslive/__init__.py index 6a6b18557b0598..64f4a0102a1572 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -1,22 +1,21 @@ """Support for Telldus Live.""" import asyncio -from functools import partial import logging +from functools import partial import voluptuous as vol -from homeassistant import config_entries -from homeassistant.const import CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, \ - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION import homeassistant.helpers.config_validation as cv +from homeassistant import config_entries +from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later - from . import config_flow # noqa pylint_disable=unused-import from .const import ( CONF_HOST, DOMAIN, KEY_SCAN_INTERVAL, KEY_SESSION, MIN_UPDATE_INTERVAL, NOT_SO_PRIVATE_KEY, PUBLIC_KEY, SCAN_INTERVAL, - SIGNAL_UPDATE_ENTITY, TELLDUS_DISCOVERY_NEW) + SIGNAL_UPDATE_ENTITY, TELLDUS_DISCOVERY_NEW +) APPLICATION_NAME = 'Home Assistant' @@ -25,21 +24,11 @@ _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Optional(CONF_HOST, default=DOMAIN): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), - vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): - vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=SCAN_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Optional(CONF_HOST, default=DOMAIN): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): + vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), + }) }, extra=vol.ALLOW_EXTRA) DATA_CONFIG_ENTRY_LOCK = 'tellduslive_config_entry_lock' diff --git a/homeassistant/components/tplink_lte/__init__.py b/homeassistant/components/tplink_lte/__init__.py index d0f6e600a0deef..ae0b73d1c7c2bf 100644 --- a/homeassistant/components/tplink_lte/__init__.py +++ b/homeassistant/components/tplink_lte/__init__.py @@ -6,10 +6,10 @@ import attr import voluptuous as vol -from homeassistant.components.notify import ATTR_TARGET from homeassistant.const import ( - CONF_HOST, CONF_NAME, CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP, - CONF_RECIPIENT) + CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_RECIPIENT, + EVENT_HOMEASSISTANT_STOP +) from homeassistant.core import callback from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_create_clientsession @@ -23,22 +23,10 @@ CONF_NOTIFY = 'notify' -# Deprecated in 0.88.0, invalidated in 0.91.0, remove in 0.92.0 -ATTR_TARGET_INVALIDATION_VERSION = '0.91.0' - -_NOTIFY_SCHEMA = vol.All( - vol.Schema({ - vol.Optional(CONF_NAME): cv.string, - vol.Optional(ATTR_TARGET): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_RECIPIENT): vol.All(cv.ensure_list, [cv.string]) - }), - cv.deprecated( - ATTR_TARGET, - replacement_key=CONF_RECIPIENT, - invalidation_version=ATTR_TARGET_INVALIDATION_VERSION - ), - cv.has_at_least_one_key(CONF_RECIPIENT), -) +_NOTIFY_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_RECIPIENT): vol.All(cv.ensure_list, [cv.string]) +}) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.All(cv.ensure_list, [vol.Schema({ diff --git a/homeassistant/components/ups/sensor.py b/homeassistant/components/ups/sensor.py index f338e990b00fff..3ed82de41dbb95 100644 --- a/homeassistant/components/ups/sensor.py +++ b/homeassistant/components/ups/sensor.py @@ -1,20 +1,19 @@ """Sensor for UPS packages.""" -from collections import defaultdict import logging +from collections import defaultdict from datetime import timedelta import voluptuous as vol +import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_NAME, CONF_USERNAME, CONF_PASSWORD, - ATTR_ATTRIBUTION, CONF_UPDATE_INTERVAL, - CONF_SCAN_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_NAME, CONF_PASSWORD, CONF_SCAN_INTERVAL, + CONF_USERNAME +) from homeassistant.helpers.entity import Entity -from homeassistant.util import slugify -from homeassistant.util import Throttle +from homeassistant.util import Throttle, slugify from homeassistant.util.dt import now, parse_date -import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['upsmychoice==1.0.6'] @@ -27,21 +26,11 @@ SCAN_INTERVAL = timedelta(seconds=1800) -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): ( - vol.All(cv.time_period, cv.positive_timedelta)), - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=SCAN_INTERVAL - ) -) +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/volvooncall/__init__.py b/homeassistant/components/volvooncall/__init__.py index 7e72607c2f3e88..36e3959338e746 100644 --- a/homeassistant/components/volvooncall/__init__.py +++ b/homeassistant/components/volvooncall/__init__.py @@ -1,21 +1,20 @@ """Support for Volvo On Call.""" -from datetime import timedelta import logging +from datetime import timedelta import voluptuous as vol -from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, - CONF_NAME, CONF_RESOURCES, - CONF_UPDATE_INTERVAL, CONF_SCAN_INTERVAL, - CONF_UPDATE_INTERVAL_INVALIDATION_VERSION) -from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import async_track_point_in_utc_time +from homeassistant.const import ( + CONF_NAME, CONF_PASSWORD, CONF_RESOURCES, CONF_SCAN_INTERVAL, CONF_USERNAME +) +from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.dispatcher import ( - async_dispatcher_send, - async_dispatcher_connect) + async_dispatcher_connect, async_dispatcher_send +) +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow DOMAIN = 'volvooncall' @@ -84,30 +83,20 @@ ] CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All( - vol.Schema({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_UPDATE_INTERVAL): - vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): - vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), - vol.Optional(CONF_NAME, default={}): - cv.schema_with_slug_keys(cv.string), - vol.Optional(CONF_RESOURCES): vol.All( - cv.ensure_list, [vol.In(RESOURCES)]), - vol.Optional(CONF_REGION): cv.string, - vol.Optional(CONF_SERVICE_URL): cv.string, - vol.Optional(CONF_MUTABLE, default=True): cv.boolean, - vol.Optional(CONF_SCANDINAVIAN_MILES, default=False): cv.boolean, - }), - cv.deprecated( - CONF_UPDATE_INTERVAL, - replacement_key=CONF_SCAN_INTERVAL, - invalidation_version=CONF_UPDATE_INTERVAL_INVALIDATION_VERSION, - default=DEFAULT_UPDATE_INTERVAL - ) - ) + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): + vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)), + vol.Optional(CONF_NAME, default={}): + cv.schema_with_slug_keys(cv.string), + vol.Optional(CONF_RESOURCES): vol.All( + cv.ensure_list, [vol.In(RESOURCES)]), + vol.Optional(CONF_REGION): cv.string, + vol.Optional(CONF_SERVICE_URL): cv.string, + vol.Optional(CONF_MUTABLE, default=True): cv.boolean, + vol.Optional(CONF_SCANDINAVIAN_MILES, default=False): cv.boolean, + }) }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/const.py b/homeassistant/const.py index e0130f3ccefadf..295a73a0e6c474 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -147,11 +147,6 @@ CONF_TYPE = 'type' CONF_UNIT_OF_MEASUREMENT = 'unit_of_measurement' CONF_UNIT_SYSTEM = 'unit_system' - -# Deprecated in 0.88.0, invalidated in 0.91.0, remove in 0.92.0 -CONF_UPDATE_INTERVAL = 'update_interval' -CONF_UPDATE_INTERVAL_INVALIDATION_VERSION = '0.91.0' - CONF_URL = 'url' CONF_USERNAME = 'username' CONF_VALUE_TEMPLATE = 'value_template' From 0c284161eb0d26120816c4909ac8230ee555ad1c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 23:45:09 -0700 Subject: [PATCH 401/605] Validate manifests in CI (#22708) * Validate manifests * Fix mode * Activate venv * Validate manifests after installing HA which includes voluptuous --- .circleci/config.yml | 7 +++ script/manifest/validate.py | 86 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100755 script/manifest/validate.py diff --git a/.circleci/config.yml b/.circleci/config.yml index b1fdc2be93b142..d31fd512abd3e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -99,6 +99,13 @@ jobs: mypy $TYPING_FILES - install + + - run: + name: validate manifests + command: | + . venv/bin/activate + python script/manifest/validate.py + - run: name: run gen_requirements_all command: | diff --git a/script/manifest/validate.py b/script/manifest/validate.py new file mode 100755 index 00000000000000..d0d59529d208df --- /dev/null +++ b/script/manifest/validate.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Validate all integrations have manifests and that they are valid.""" +import json +import pathlib +import sys + +import voluptuous as vol +from voluptuous.humanize import humanize_error + + +MANIFEST_SCHEMA = vol.Schema({ + vol.Required('domain'): str, + vol.Required('name'): str, + vol.Required('documentation'): str, + vol.Required('requirements'): [str], + vol.Required('dependencies'): [str], + vol.Required('codeowners'): [str], +}) + + +components_path = pathlib.Path('homeassistant/components') + + +def validate_integration(path): + """Validate that an integrations has a valid manifest.""" + errors = [] + path = pathlib.Path(path) + + manifest_path = path / 'manifest.json' + + if not manifest_path.is_file(): + errors.append('File manifest.json not found') + return errors # Fatal error + + try: + manifest = json.loads(manifest_path.read_text()) + except ValueError as err: + errors.append("Manifest contains invalid JSON: {}".format(err)) + return errors # Fatal error + + try: + MANIFEST_SCHEMA(manifest) + except vol.Invalid as err: + errors.append(humanize_error(manifest, err)) + + if manifest['domain'] != path.name: + errors.append('Domain does not match dir name') + + for dep in manifest['dependencies']: + dep_manifest = path.parent / dep / 'manifest.json' + if not dep_manifest.is_file(): + errors.append("Unable to find dependency {}".format(dep)) + + return errors + + +def validate_all(): + """Validate all integrations.""" + invalid = [] + + for fil in components_path.iterdir(): + if fil.is_file() or fil.name == '__pycache__': + continue + + errors = validate_integration(fil) + + if errors: + invalid.append((fil, errors)) + + if not invalid: + return 0 + + print("Found invalid manifests") + print() + + for integration, errors in invalid: + print(integration) + for error in errors: + print("*", error) + print() + + return 1 + + +if __name__ == '__main__': + sys.exit(validate_all()) From d5307c03d89df3d100198ab7214444199ae091ea Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 3 Apr 2019 23:46:06 -0700 Subject: [PATCH 402/605] Generate codeowners based on manifests (#22705) * Gen codeowners * Update manifest_helper.py --- .circleci/config.yml | 6 + CODEOWNERS | 342 ++++++++++++----------------- script/manifest/codeowners.py | 66 ++++++ script/manifest/manifest_helper.py | 15 ++ 4 files changed, 233 insertions(+), 196 deletions(-) create mode 100755 script/manifest/codeowners.py create mode 100644 script/manifest/manifest_helper.py diff --git a/.circleci/config.yml b/.circleci/config.yml index d31fd512abd3e3..70d2f7af3a32fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,6 +91,12 @@ jobs: . venv/bin/activate flake8 + - run: + name: validate CODEOWNERS + command: | + . venv/bin/activate + python script/manifest/codeowners.py validate + - run: name: run static type check command: | diff --git a/CODEOWNERS b/CODEOWNERS index 5be5610a5c7ff5..ae5bcb452cb7e8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,3 +1,4 @@ +# This file is generated by script/manifest/codeowners.py # People marked here will be automatically requested for a review # when the code that they own is touched. # https://github.com/blog/2392-introducing-code-owners @@ -7,288 +8,237 @@ setup.py @home-assistant/core homeassistant/*.py @home-assistant/core homeassistant/helpers/* @home-assistant/core homeassistant/util/* @home-assistant/core -homeassistant/components/api/* @home-assistant/core -homeassistant/components/auth/* @home-assistant/core -homeassistant/components/automation/* @home-assistant/core -homeassistant/components/cloud/* @home-assistant/core -homeassistant/components/config/* @home-assistant/core -homeassistant/components/configurator/* @home-assistant/core -homeassistant/components/conversation/* @home-assistant/core -homeassistant/components/frontend/* @home-assistant/core -homeassistant/components/group/* @home-assistant/core -homeassistant/components/history/* @home-assistant/core -homeassistant/components/http/* @home-assistant/core -homeassistant/components/input_boolean/* @home-assistant/core -homeassistant/components/input_datetime/* @home-assistant/core -homeassistant/components/input_number/* @home-assistant/core -homeassistant/components/input_select/* @home-assistant/core -homeassistant/components/input_text/* @home-assistant/core -homeassistant/components/introduction/* @home-assistant/core -homeassistant/components/logger/* @home-assistant/core -homeassistant/components/lovelace/* @home-assistant/core -homeassistant/components/mqtt/* @home-assistant/core -homeassistant/components/panel_custom/* @home-assistant/core -homeassistant/components/panel_iframe/* @home-assistant/core -homeassistant/components/onboarding/* @home-assistant/core -homeassistant/components/persistent_notification/* @home-assistant/core -homeassistant/components/scene/__init__.py @home-assistant/core -homeassistant/components/scene/homeassistant.py @home-assistant/core -homeassistant/components/script/* @home-assistant/core -homeassistant/components/shell_command/* @home-assistant/core -homeassistant/components/sun/* @home-assistant/core -homeassistant/components/updater/* @home-assistant/core -homeassistant/components/weblink/* @home-assistant/core -homeassistant/components/websocket_api/* @home-assistant/core -homeassistant/components/zone/* @home-assistant/core -# Home Assistant Developer Teams +# Virtualization Dockerfile @home-assistant/docker virtualization/Docker/* @home-assistant/docker -homeassistant/components/zwave/* @home-assistant/z-wave - -homeassistant/components/hassio/* @home-assistant/hassio - -# Individual platforms +# Other code +homeassistant/scripts/check_config.py @kellerza -# A -homeassistant/components/airvisual/sensor.py @bachya -homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell -homeassistant/components/alpha_vantage/sensor.py @fabaff +# Integrations +homeassistant/components/airvisual/* @bachya +homeassistant/components/alarm_control_panel/* @colinodell +homeassistant/components/alpha_vantage/* @fabaff homeassistant/components/amazon_polly/* @robbiet480 homeassistant/components/ambient_station/* @bachya +homeassistant/components/api/* @home-assistant/core homeassistant/components/arduino/* @fabaff homeassistant/components/arest/* @fabaff -homeassistant/components/asuswrt/device_tracker.py @kennedyshead -homeassistant/components/automatic/device_tracker.py @armills +homeassistant/components/asuswrt/* @kennedyshead +homeassistant/components/auth/* @home-assistant/core +homeassistant/components/automatic/* @armills +homeassistant/components/automation/* @home-assistant/core homeassistant/components/aws/* @awarecan @robbiet480 homeassistant/components/axis/* @kane610 - -# B -homeassistant/components/bitcoin/sensor.py @fabaff +homeassistant/components/bitcoin/* @fabaff homeassistant/components/blink/* @fronzbot homeassistant/components/bmw_connected_drive/* @ChristianKuehnel -homeassistant/components/braviatv/media_player.py @robbiet480 +homeassistant/components/braviatv/* @robbiet480 homeassistant/components/broadlink/* @danielhiversen -homeassistant/components/brunt/cover.py @eavanvalkenburg -homeassistant/components/bt_smarthub/device_tracker.py @jxwolstenholme - -# C +homeassistant/components/brunt/* @eavanvalkenburg +homeassistant/components/bt_smarthub/* @jxwolstenholme +homeassistant/components/cloud/* @home-assistant/core homeassistant/components/cloudflare/* @ludeeus -homeassistant/components/coolmaster/climate.py @OnFreund +homeassistant/components/config/* @home-assistant/core +homeassistant/components/configurator/* @home-assistant/core +homeassistant/components/conversation/* @home-assistant/core +homeassistant/components/coolmaster/* @OnFreund homeassistant/components/counter/* @fabaff -homeassistant/components/cover/group.py @cdce8p -homeassistant/components/cpuspeed/sensor.py @fabaff -homeassistant/components/cups/sensor.py @fabaff - -# D +homeassistant/components/cover/* @cdce8p +homeassistant/components/cpuspeed/* @fabaff +homeassistant/components/cups/* @fabaff homeassistant/components/daikin/* @fredrike @rofrantz homeassistant/components/darksky/* @fabaff -homeassistant/components/discogs/sensor.py @thibmaek homeassistant/components/deconz/* @kane610 -homeassistant/components/demo/weather.py @fabaff +homeassistant/components/demo/* @fabaff homeassistant/components/digital_ocean/* @fabaff +homeassistant/components/discogs/* @thibmaek homeassistant/components/doorbird/* @oblogic7 homeassistant/components/dweet/* @fabaff - -# E homeassistant/components/ecovacs/* @OverloadUT homeassistant/components/edp_redy/* @abmantis -homeassistant/components/eight_sleep/* @mezz64 homeassistant/components/egardia/* @jeroenterheerdt -homeassistant/components/emby/media_player.py @mezz64 -homeassistant/components/ephember/climate.py @ttroy50 -homeassistant/components/eq3btsmart/climate.py @rytilahti +homeassistant/components/eight_sleep/* @mezz64 +homeassistant/components/emby/* @mezz64 +homeassistant/components/ephember/* @ttroy50 +homeassistant/components/eq3btsmart/* @rytilahti homeassistant/components/esphome/* @OttoWinter - -# F homeassistant/components/file/* @fabaff -homeassistant/components/filter/sensor.py @dgomes -homeassistant/components/fitbit/sensor.py @robbiet480 -homeassistant/components/fixer/sensor.py @fabaff -homeassistant/components/flock/notify.py @fabaff -homeassistant/components/flunearyou/sensor.py @bachya +homeassistant/components/filter/* @dgomes +homeassistant/components/fitbit/* @robbiet480 +homeassistant/components/fixer/* @fabaff +homeassistant/components/flock/* @fabaff +homeassistant/components/flunearyou/* @bachya homeassistant/components/foursquare/* @robbiet480 homeassistant/components/freebox/* @snoof85 - -# G -homeassistant/components/gearbest/sensor.py @HerrHofrat -homeassistant/components/gitter/sensor.py @fabaff -homeassistant/components/glances/sensor.py @fabaff -homeassistant/components/gntp/notify.py @robbiet480 -homeassistant/components/google_travel_time/sensor.py @robbiet480 +homeassistant/components/frontend/* @home-assistant/core +homeassistant/components/gearbest/* @HerrHofrat +homeassistant/components/gitter/* @fabaff +homeassistant/components/glances/* @fabaff +homeassistant/components/gntp/* @robbiet480 +homeassistant/components/google_travel_time/* @robbiet480 homeassistant/components/googlehome/* @ludeeus -homeassistant/components/gpsd/sensor.py @fabaff -homeassistant/components/gtfs/sensor.py @robbiet480 - -# H +homeassistant/components/gpsd/* @fabaff +homeassistant/components/group/* @home-assistant/core +homeassistant/components/gtfs/* @robbiet480 homeassistant/components/harmony/* @ehendrix23 +homeassistant/components/hassio/* @home-assistant/hassio homeassistant/components/heos/* @andrewsayre -homeassistant/components/hikvision/binary_sensor.py @mezz64 +homeassistant/components/hikvision/* @mezz64 +homeassistant/components/history/* @home-assistant/core homeassistant/components/history_graph/* @andrey-git homeassistant/components/hive/* @Rendili @KJonline +homeassistant/components/homeassistant/* @home-assistant/core homeassistant/components/homekit/* @cdce8p -homeassistant/components/html5/notify.py @robbiet480 +homeassistant/components/html5/* @robbiet480 +homeassistant/components/http/* @home-assistant/core homeassistant/components/huawei_lte/* @scop -homeassistant/components/huawei_router/device_tracker.py @abmantis - -# I +homeassistant/components/huawei_router/* @abmantis +homeassistant/components/hue/* @balloob homeassistant/components/influxdb/* @fabaff -homeassistant/components/integration/sensor.py @dgomes +homeassistant/components/input_boolean/* @home-assistant/core +homeassistant/components/input_datetime/* @home-assistant/core +homeassistant/components/input_number/* @home-assistant/core +homeassistant/components/input_select/* @home-assistant/core +homeassistant/components/input_text/* @home-assistant/core +homeassistant/components/integration/* @dgomes +homeassistant/components/introduction/* @home-assistant/core homeassistant/components/ios/* @robbiet480 homeassistant/components/ipma/* @dgomes -homeassistant/components/irish_rail_transport/sensor.py @ttroy50 - -# J -homeassistant/components/jewish_calendar/sensor.py @tsvi - -# K +homeassistant/components/irish_rail_transport/* @ttroy50 +homeassistant/components/jewish_calendar/* @tsvi homeassistant/components/knx/* @Julius2342 -homeassistant/components/kodi/media_player.py @armills +homeassistant/components/kodi/* @armills homeassistant/components/konnected/* @heythisisnate - -# L -homeassistant/components/lametric/notify.py @robbiet480 -homeassistant/components/launch_library/sensor.py @ludeeus +homeassistant/components/lametric/* @robbiet480 +homeassistant/components/launch_library/* @ludeeus homeassistant/components/lifx/* @amelchio -homeassistant/components/lifx_cloud/scene.py @amelchio -homeassistant/components/lifx_legacy/light.py @amelchio -homeassistant/components/linux_battery/sensor.py @fabaff -homeassistant/components/liveboxplaytv/media_player.py @pschmitt +homeassistant/components/lifx_cloud/* @amelchio +homeassistant/components/lifx_legacy/* @amelchio +homeassistant/components/linux_battery/* @fabaff +homeassistant/components/liveboxplaytv/* @pschmitt +homeassistant/components/logger/* @home-assistant/core +homeassistant/components/lovelace/* @home-assistant/core homeassistant/components/luftdaten/* @fabaff - -# M -homeassistant/components/mastodon/notify.py @fabaff +homeassistant/components/mastodon/* @fabaff homeassistant/components/matrix/* @tinloaf -homeassistant/components/mediaroom/media_player.py @dgomes +homeassistant/components/mediaroom/* @dgomes homeassistant/components/melissa/* @kennedyshead -homeassistant/components/met/weather.py @danielhiversen -homeassistant/components/miflora/sensor.py @danielhiversen @ChristianKuehnel -homeassistant/components/mill/climate.py @danielhiversen -homeassistant/components/min_max/sensor.py @fabaff +homeassistant/components/met/* @danielhiversen +homeassistant/components/miflora/* @danielhiversen @ChristianKuehnel +homeassistant/components/mill/* @danielhiversen +homeassistant/components/min_max/* @fabaff homeassistant/components/mobile_app/* @robbiet480 -homeassistant/components/monoprice/media_player.py @etsinko -homeassistant/components/moon/sensor.py @fabaff -homeassistant/components/mpd/media_player.py @fabaff +homeassistant/components/monoprice/* @etsinko +homeassistant/components/moon/* @fabaff +homeassistant/components/mpd/* @fabaff +homeassistant/components/mqtt/* @home-assistant/core homeassistant/components/mystrom/* @fabaff - -# N -homeassistant/components/nello/lock.py @pschmitt +homeassistant/components/nello/* @pschmitt homeassistant/components/ness_alarm/* @nickw444 homeassistant/components/nest/* @awarecan -homeassistant/components/netdata/sensor.py @fabaff +homeassistant/components/netdata/* @fabaff homeassistant/components/nissan_leaf/* @filcole -homeassistant/components/nmbs/sensor.py @thibmaek +homeassistant/components/nmbs/* @thibmaek homeassistant/components/no_ip/* @fabaff -homeassistant/components/nuki/lock.py @pschmitt -homeassistant/components/nsw_fuel_station/sensor.py @nickw444 - -# O -homeassistant/components/ohmconnect/sensor.py @robbiet480 +homeassistant/components/notify/* @flowolf +homeassistant/components/nsw_fuel_station/* @nickw444 +homeassistant/components/nuki/* @pschmitt +homeassistant/components/ohmconnect/* @robbiet480 +homeassistant/components/onboarding/* @home-assistant/core homeassistant/components/openuv/* @bachya -homeassistant/components/openweathermap/weather.py @fabaff +homeassistant/components/openweathermap/* @fabaff homeassistant/components/owlet/* @oblogic7 - -# P -homeassistant/components/pi_hole/sensor.py @fabaff +homeassistant/components/panel_custom/* @home-assistant/core +homeassistant/components/panel_iframe/* @home-assistant/core +homeassistant/components/persistent_notification/* @home-assistant/core +homeassistant/components/pi_hole/* @fabaff homeassistant/components/plant/* @ChristianKuehnel homeassistant/components/point/* @fredrike -homeassistant/components/pollen/sensor.py @bachya -homeassistant/components/push/camera.py @dgomes -homeassistant/components/pvoutput/sensor.py @fabaff - -# Q -homeassistant/components/qnap/sensor.py @colinodell -homeassistant/components/quantum_gateway/device_tracker.py @cisasteelersfan +homeassistant/components/pollen/* @bachya +homeassistant/components/push/* @dgomes +homeassistant/components/pvoutput/* @fabaff +homeassistant/components/qnap/* @colinodell +homeassistant/components/quantum_gateway/* @cisasteelersfan homeassistant/components/qwikswitch/* @kellerza - -# R homeassistant/components/rainmachine/* @bachya homeassistant/components/random/* @fabaff homeassistant/components/rfxtrx/* @danielhiversen homeassistant/components/rmvtransport/* @cgtobi -homeassistant/components/roomba/vacuum.py @pschmitt -homeassistant/components/ruter/sensor.py @ludeeus - -# S -homeassistant/components/scrape/sensor.py @fabaff -homeassistant/components/sensibo/climate.py @andrey-git -homeassistant/components/serial/sensor.py @fabaff -homeassistant/components/seventeentrack/sensor.py @bachya +homeassistant/components/roomba/* @pschmitt +homeassistant/components/ruter/* @ludeeus +homeassistant/components/scene/* @home-assistant/core +homeassistant/components/scrape/* @fabaff +homeassistant/components/script/* @home-assistant/core +homeassistant/components/sensibo/* @andrey-git +homeassistant/components/serial/* @fabaff +homeassistant/components/seventeentrack/* @bachya +homeassistant/components/shell_command/* @home-assistant/core homeassistant/components/shiftr/* @fabaff -homeassistant/components/shodan/sensor.py @fabaff +homeassistant/components/shodan/* @fabaff homeassistant/components/simplisafe/* @bachya -homeassistant/components/sma/sensor.py @kellerza +homeassistant/components/sma/* @kellerza homeassistant/components/smartthings/* @andrewsayre -homeassistant/components/smtp/notify.py @fabaff +homeassistant/components/smtp/* @fabaff homeassistant/components/sonos/* @amelchio homeassistant/components/spaceapi/* @fabaff homeassistant/components/spider/* @peternijssen -homeassistant/components/sql/sensor.py @dgomes -homeassistant/components/statistics/sensor.py @fabaff -homeassistant/components/swiss_public_transport/* @fabaff +homeassistant/components/sql/* @dgomes +homeassistant/components/statistics/* @fabaff +homeassistant/components/sun/* @home-assistant/core homeassistant/components/swiss_hydrological_data/* @fabaff -homeassistant/components/switchbot/switch.py @danielhiversen -homeassistant/components/switchmate/switch.py @danielhiversen -homeassistant/components/synology_srm/device_tracker.py @aerialls -homeassistant/components/syslog/notify.py @fabaff -homeassistant/components/sytadin/sensor.py @gautric - -# T +homeassistant/components/swiss_public_transport/* @fabaff +homeassistant/components/switchbot/* @danielhiversen +homeassistant/components/switchmate/* @danielhiversen +homeassistant/components/synology_srm/* @aerialls +homeassistant/components/syslog/* @fabaff +homeassistant/components/sytadin/* @gautric homeassistant/components/tahoma/* @philklei -homeassistant/components/tautulli/sensor.py @ludeeus +homeassistant/components/tautulli/* @ludeeus homeassistant/components/tellduslive/* @fredrike -homeassistant/components/template/cover.py @PhracturedBlue +homeassistant/components/template/* @PhracturedBlue homeassistant/components/tesla/* @zabuldon homeassistant/components/tfiac/* @fredrike @mellado homeassistant/components/thethingsnetwork/* @fabaff -homeassistant/components/threshold/binary_sensor.py @fabaff +homeassistant/components/threshold/* @fabaff homeassistant/components/tibber/* @danielhiversen -homeassistant/components/tile/device_tracker.py @bachya -homeassistant/components/time_date/sensor.py @fabaff +homeassistant/components/tile/* @bachya +homeassistant/components/time_date/* @fabaff homeassistant/components/toon/* @frenck homeassistant/components/tplink/* @rytilahti -homeassistant/components/traccar/device_tracker.py @ludeeus +homeassistant/components/traccar/* @ludeeus homeassistant/components/tradfri/* @ggravlingen -homeassistant/components/twilio_call/notify.py @robbiet480 -homeassistant/components/twilio_sms/notify.py @robbiet480 - -# U -homeassistant/components/uber/sensor.py @robbiet480 +homeassistant/components/tts/* @robbiet480 +homeassistant/components/twilio_call/* @robbiet480 +homeassistant/components/twilio_sms/* @robbiet480 +homeassistant/components/uber/* @robbiet480 homeassistant/components/unifi/* @kane610 homeassistant/components/upcloud/* @scop +homeassistant/components/updater/* @home-assistant/core homeassistant/components/upnp/* @robbiet480 -homeassistant/components/uptimerobot/binary_sensor.py @ludeeus +homeassistant/components/uptimerobot/* @ludeeus homeassistant/components/utility_meter/* @dgomes - -# V homeassistant/components/velux/* @Julius2342 -homeassistant/components/version/sensor.py @fabaff - -# W -homeassistant/components/waqi/sensor.py @andrey-git -homeassistant/components/weather/__init__.py @fabaff +homeassistant/components/version/* @fabaff +homeassistant/components/waqi/* @andrey-git +homeassistant/components/weather/* @fabaff +homeassistant/components/weblink/* @home-assistant/core +homeassistant/components/websocket_api/* @home-assistant/core homeassistant/components/wemo/* @sqldiablo -homeassistant/components/worldclock/sensor.py @fabaff - -# X -homeassistant/components/xfinity/device_tracker.py @cisasteelersfan +homeassistant/components/worldclock/* @fabaff +homeassistant/components/xfinity/* @cisasteelersfan homeassistant/components/xiaomi_aqara/* @danielhiversen @syssi homeassistant/components/xiaomi_miio/* @rytilahti @syssi -homeassistant/components/xiaomi_tv/media_player.py @fattdev -homeassistant/components/xmpp/notify.py @fabaff - -# Y +homeassistant/components/xiaomi_tv/* @fattdev +homeassistant/components/xmpp/* @fabaff homeassistant/components/yamaha_musiccast/* @jalmeroth homeassistant/components/yeelight/* @rytilahti @zewelor -homeassistant/components/yeelightsunflower/light.py @lindsaymarkward -homeassistant/components/yessssms/notify.py @flowolf -homeassistant/components/yi/camera.py @bachya - -# Z +homeassistant/components/yeelightsunflower/* @lindsaymarkward +homeassistant/components/yessssms/* @flowolf +homeassistant/components/yi/* @bachya homeassistant/components/zeroconf/* @robbiet480 homeassistant/components/zha/* @dmulcahey @adminiuga +homeassistant/components/zone/* @home-assistant/core homeassistant/components/zoneminder/* @rohankapoorcom - -# Other code -homeassistant/scripts/check_config.py @kellerza +homeassistant/components/zwave/* @home-assistant/z-wave diff --git a/script/manifest/codeowners.py b/script/manifest/codeowners.py new file mode 100755 index 00000000000000..9745f3b82f2f4a --- /dev/null +++ b/script/manifest/codeowners.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Generate CODEOWNERS.""" +import os +import sys + +from manifest_helper import iter_manifests + +BASE = """ +# This file is generated by script/manifest/codeowners.py +# People marked here will be automatically requested for a review +# when the code that they own is touched. +# https://github.com/blog/2392-introducing-code-owners + +# Home Assistant Core +setup.py @home-assistant/core +homeassistant/*.py @home-assistant/core +homeassistant/helpers/* @home-assistant/core +homeassistant/util/* @home-assistant/core + +# Virtualization +Dockerfile @home-assistant/docker +virtualization/Docker/* @home-assistant/docker + +# Other code +homeassistant/scripts/check_config.py @kellerza + +# Integrations +""" + + +def generate(): + """Generate CODEOWNERS.""" + parts = [BASE.strip()] + + for manifest in iter_manifests(): + if not manifest['codeowners']: + continue + + parts.append("homeassistant/components/{}/* {}".format( + manifest['domain'], ' '.join(manifest['codeowners']))) + + return '\n'.join(parts) + + +def main(validate): + """Runner for CODEOWNERS gen.""" + if not os.path.isfile('requirements_all.txt'): + print('Run this from HA root dir') + return 1 + + content = generate() + + if validate: + with open('CODEOWNERS', 'r') as fp: + if fp.read().strip() != content: + print("CODEOWNERS is not up to date. " + "Run python script/manifest/codeowners.py") + return 1 + return 0 + + with open('CODEOWNERS', 'w') as fp: + fp.write(content + '\n') + + +if __name__ == '__main__': + sys.exit(main(sys.argv[-1] == 'validate')) diff --git a/script/manifest/manifest_helper.py b/script/manifest/manifest_helper.py new file mode 100644 index 00000000000000..3b4cfa11796255 --- /dev/null +++ b/script/manifest/manifest_helper.py @@ -0,0 +1,15 @@ +"""Helpers to deal with manifests.""" +import json +import pathlib + + +component_dir = pathlib.Path('homeassistant/components') + + +def iter_manifests(): + """Iterate over all available manifests.""" + manifests = [ + json.loads(fil.read_text()) + for fil in component_dir.glob('*/manifest.json') + ] + return sorted(manifests, key=lambda man: man['domain']) From f9564400e8c3cbd00d4dd9ad68ce9076316763c9 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 3 Apr 2019 23:53:17 -0700 Subject: [PATCH 403/605] Activate codeowners-mention via GitHub actions --- .github/main.workflow | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/main.workflow diff --git a/.github/main.workflow b/.github/main.workflow new file mode 100644 index 00000000000000..62336ebf126264 --- /dev/null +++ b/.github/main.workflow @@ -0,0 +1,14 @@ +workflow "Mention CODEOWNERS of integrations when integration label is added to an issue" { + on = "issues" + resolves = "codeowners-mention" +} + +workflow "Mention CODEOWNERS of integrations when integration label is added to an PRs" { + on = "pull_request" + resolves = "codeowners-mention" +} + +action "codeowners-mention" { + uses = "home-assistant/codeowners-mention@master" + secrets = ["GITHUB_TOKEN"] +} From 704983a64f4d11dceff4180d084da8d00e71421a Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 4 Apr 2019 00:34:34 -0700 Subject: [PATCH 404/605] Fix hassio CODEOWNER to be the actual team name, hass-io --- CODEOWNERS | 2 +- homeassistant/components/hassio/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index ae5bcb452cb7e8..5adc4d071c2c6f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -83,7 +83,7 @@ homeassistant/components/gpsd/* @fabaff homeassistant/components/group/* @home-assistant/core homeassistant/components/gtfs/* @robbiet480 homeassistant/components/harmony/* @ehendrix23 -homeassistant/components/hassio/* @home-assistant/hassio +homeassistant/components/hassio/* @home-assistant/hass-io homeassistant/components/heos/* @andrewsayre homeassistant/components/hikvision/* @mezz64 homeassistant/components/history/* @home-assistant/core diff --git a/homeassistant/components/hassio/manifest.json b/homeassistant/components/hassio/manifest.json index e412f587abd616..be345fb5adbc17 100644 --- a/homeassistant/components/hassio/manifest.json +++ b/homeassistant/components/hassio/manifest.json @@ -7,6 +7,6 @@ "http" ], "codeowners": [ - "@home-assistant/hassio" + "@home-assistant/hass-io" ] } From d231d598963a79ad77863abb4ee36681447bd0b2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 4 Apr 2019 00:46:20 -0700 Subject: [PATCH 405/605] Remove deprecated Insteon components (#22710) --- .../components/insteon_local/__init__.py | 23 ------------------- .../components/insteon_local/manifest.json | 8 ------- .../components/insteon_plm/__init__.py | 23 ------------------- .../components/insteon_plm/manifest.json | 8 ------- 4 files changed, 62 deletions(-) delete mode 100644 homeassistant/components/insteon_local/__init__.py delete mode 100644 homeassistant/components/insteon_local/manifest.json delete mode 100644 homeassistant/components/insteon_plm/__init__.py delete mode 100644 homeassistant/components/insteon_plm/manifest.json diff --git a/homeassistant/components/insteon_local/__init__.py b/homeassistant/components/insteon_local/__init__.py deleted file mode 100644 index f73c46746f02c1..00000000000000 --- a/homeassistant/components/insteon_local/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Local support for Insteon.""" -import logging - -_LOGGER = logging.getLogger(__name__) - - -def setup(hass, config): - """Set up the insteon_local component. - - This component is deprecated as of release 0.77 and should be removed in - release 0.90. - """ - _LOGGER.warning('The insteon_local component has been replaced by ' - 'the insteon component') - _LOGGER.warning('Please see https://home-assistant.io/components/insteon') - - hass.components.persistent_notification.create( - 'insteon_local has been replaced by the insteon component.
' - 'Please see https://home-assistant.io/components/insteon', - title='insteon_local Component Deactivated', - notification_id='insteon_local') - - return False diff --git a/homeassistant/components/insteon_local/manifest.json b/homeassistant/components/insteon_local/manifest.json deleted file mode 100644 index 64b6bccdba63c0..00000000000000 --- a/homeassistant/components/insteon_local/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "domain": "insteon_local", - "name": "Insteon local", - "documentation": "https://www.home-assistant.io/components/insteon_local", - "requirements": [], - "dependencies": [], - "codeowners": [] -} diff --git a/homeassistant/components/insteon_plm/__init__.py b/homeassistant/components/insteon_plm/__init__.py deleted file mode 100644 index 5ff492b6f6c39c..00000000000000 --- a/homeassistant/components/insteon_plm/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Support for INSTEON PowerLinc Modem.""" -import logging - -_LOGGER = logging.getLogger(__name__) - - -async def async_setup(hass, config): - """Set up the insteon_plm component. - - This component is deprecated as of release 0.77 and should be removed in - release 0.90. - """ - _LOGGER.warning('The insteon_plm component has been replaced by ' - 'the insteon component') - _LOGGER.warning('Please see https://home-assistant.io/components/insteon') - - hass.components.persistent_notification.create( - 'insteon_plm has been replaced by the insteon component.
' - 'Please see https://home-assistant.io/components/insteon', - title='insteon_plm Component Deactivated', - notification_id='insteon_plm') - - return False diff --git a/homeassistant/components/insteon_plm/manifest.json b/homeassistant/components/insteon_plm/manifest.json deleted file mode 100644 index fa382dd2df020e..00000000000000 --- a/homeassistant/components/insteon_plm/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "domain": "insteon_plm", - "name": "Insteon plm", - "documentation": "https://www.home-assistant.io/components/insteon_plm", - "requirements": [], - "dependencies": [], - "codeowners": [] -} From beb6ddfa68e6f1c1ef836af863f2c1a168d632b7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 4 Apr 2019 11:10:44 +0200 Subject: [PATCH 406/605] Change URL handling (#22713) --- homeassistant/components/hassio/ingress.py | 28 +++++++++++----------- tests/components/hassio/test_ingress.py | 10 ++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 04241f53de9c0b..9b62bb89c94f67 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -30,7 +30,7 @@ class HassIOIngress(HomeAssistantView): """Hass.io view to handle base part.""" name = "api:hassio:ingress" - url = "/api/hassio_ingress/{addon}/{path:.+}" + url = "/api/hassio_ingress/{token}/{path:.+}" requires_auth = False def __init__(self, host: str, websession: aiohttp.ClientSession): @@ -38,21 +38,21 @@ def __init__(self, host: str, websession: aiohttp.ClientSession): self._host = host self._websession = websession - def _create_url(self, addon: str, path: str) -> str: + def _create_url(self, token: str, path: str) -> str: """Create URL to service.""" - return "http://{}/addons/{}/web/{}".format(self._host, addon, path) + return "http://{}/ingress/{}/{}".format(self._host, token, path) async def _handle( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: """Route data to Hass.io ingress service.""" try: # Websocket if _is_websocket(request): - return await self._handle_websocket(request, addon, path) + return await self._handle_websocket(request, token, path) # Request - return await self._handle_request(request, addon, path) + return await self._handle_request(request, token, path) except aiohttp.ClientError: pass @@ -65,15 +65,15 @@ async def _handle( delete = _handle async def _handle_websocket( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> web.WebSocketResponse: """Ingress route for websocket.""" ws_server = web.WebSocketResponse() await ws_server.prepare(request) # Preparing - url = self._create_url(addon, path) - source_header = _init_header(request, addon) + url = self._create_url(token, path) + source_header = _init_header(request, token) # Support GET query if request.query_string: @@ -95,12 +95,12 @@ async def _handle_websocket( return ws_server async def _handle_request( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> Union[web.Response, web.StreamResponse]: """Ingress route for request.""" - url = self._create_url(addon, path) + url = self._create_url(token, path) data = await request.read() - source_header = _init_header(request, addon) + source_header = _init_header(request, token) async with self._websession.request( request.method, url, headers=source_header, @@ -136,7 +136,7 @@ async def _handle_request( def _init_header( - request: web.Request, addon: str + request: web.Request, token: str ) -> Union[CIMultiDict, Dict[str, str]]: """Create initial header.""" headers = {} @@ -151,7 +151,7 @@ def _init_header( headers[X_HASSIO] = os.environ.get('HASSIO_TOKEN', "") # Ingress information - headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(addon) + headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(token) # Set X-Forwarded-For forward_for = request.headers.get(hdrs.X_FORWARDED_FOR) diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 4b72eda4c2596a..0dda69b2b17b6e 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -13,7 +13,7 @@ async def test_ingress_request_get( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.get("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.get( @@ -45,7 +45,7 @@ async def test_ingress_request_get( async def test_ingress_request_post( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.post("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.post("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.post( @@ -77,7 +77,7 @@ async def test_ingress_request_post( async def test_ingress_request_put( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.put("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.put("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.put( @@ -109,7 +109,7 @@ async def test_ingress_request_put( async def test_ingress_request_delete( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.delete("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.delete("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.delete( @@ -142,7 +142,7 @@ async def test_ingress_request_delete( async def test_ingress_websocket( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.get("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1])) # Ignore error because we can setup a full IO infrastructure From 754c4d205bdf830bb9240a8973f3fb8ddec8f8d2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 4 Apr 2019 02:15:20 -0700 Subject: [PATCH 407/605] Allow users to set encoding of mikrotik connection (#22715) ## Description: Mikrotik does some stupid stuff with character encoding that can screw up the DHCP responses. See #15257 for more detail. **Related issue (if applicable):** fixes #15257 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/mikrotik/device_tracker.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index ed0734588ef035..7d376b431bbd82 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -18,13 +18,17 @@ MTK_DEFAULT_API_PORT = '8728' MTK_DEFAULT_API_SSL_PORT = '8729' +CONF_ENCODING = 'encoding' +DEFAULT_ENCODING = 'utf-8' + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Optional(CONF_METHOD): cv.string, vol.Optional(CONF_PORT): cv.port, - vol.Optional(CONF_SSL, default=False): cv.boolean + vol.Optional(CONF_SSL, default=False): cv.boolean, + vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, }) @@ -59,6 +63,7 @@ def __init__(self, config): self.client = None self.wireless_exist = None self.success_init = self.connect_to_device() + self.encoding = config[CONF_ENCODING] if self.success_init: _LOGGER.info("Start polling Mikrotik (%s) router...", self.host) @@ -72,7 +77,7 @@ def connect_to_device(self): try: kwargs = { 'port': self.port, - 'encoding': 'utf-8' + 'encoding': self.encoding } if self.ssl: ssl_context = ssl.create_default_context() From 5d7c29dee24c341e16ddf5907ae50697d442b914 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 4 Apr 2019 13:01:56 +0200 Subject: [PATCH 408/605] Only post coverage comment if coverage changes (#22721) --- .codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.codecov.yml b/.codecov.yml index 96a39e7319bee2..9ad9083506dec7 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -11,3 +11,5 @@ coverage: slack: default: url: "secret:TgWDUM4Jw0w7wMJxuxNF/yhSOHglIo1fGwInJnRLEVPy2P2aLimkoK1mtKCowH5TFw+baUXVXT3eAqefbdvIuM8BjRR4aRji95C6CYyD0QHy4N8i7nn1SQkWDPpS8IthYTg07rUDF7s5guurkKv2RrgoCdnnqjAMSzHoExMOF7xUmblMdhBTWJgBpWEhASJy85w/xxjlsE1xoTkzeJu9Q67pTXtRcn+5kb5/vIzPSYg=" +comment: + require_changes: yes From 172ede217afd2c83cd2be49de89ce773a95691dc Mon Sep 17 00:00:00 2001 From: Eliran Turgeman Date: Thu, 4 Apr 2019 15:23:31 +0300 Subject: [PATCH 409/605] Add 10 additional language options to DarkSky (#22719) --- homeassistant/components/darksky/sensor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index 0e87593b25ca66..6aee3457acb381 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -157,10 +157,11 @@ # Language Supported Codes LANGUAGE_CODES = [ - 'ar', 'az', 'be', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', - 'et', 'fi', 'fr', 'he', 'hr', 'hu', 'id', 'is', 'it', 'ja', 'ka', 'ko', - 'kw', 'lv', 'nb', 'nl', 'pl', 'pt', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', - 'tet', 'tr', 'uk', 'x-pig-latin', 'zh', 'zh-tw', + 'ar', 'az', 'be', 'bg', 'bn', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'en', + 'ja', 'ka', 'kn', 'ko', 'eo', 'es', 'et', 'fi', 'fr', 'he', 'hi', 'hr', + 'hu', 'id', 'is', 'it', 'kw', 'lv', 'ml', 'mr', 'nb', 'nl', 'pa', 'pl', + 'pt', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'ta', 'te', 'tet', 'tr', 'uk', + 'ur', 'x-pig-latin', 'zh', 'zh-tw', ] ALLOWED_UNITS = ['auto', 'si', 'us', 'ca', 'uk', 'uk2'] From e29eb4fa23fd1f2b257fe660efea4602f7d5703a Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 4 Apr 2019 09:06:54 -0400 Subject: [PATCH 410/605] fix device class lookup for binary sensors (#22724) --- homeassistant/components/zha/core/const.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 02f43a4bbf6def..193780c9124728 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -76,7 +76,6 @@ GENERIC = 'generic' UNKNOWN = 'unknown' OPENING = 'opening' -ZONE = 'zone' OCCUPANCY = 'occupancy' ACCELERATION = 'acceleration' @@ -89,7 +88,7 @@ COLOR_CHANNEL = 'light_color' FAN_CHANNEL = 'fan' LEVEL_CHANNEL = ATTR_LEVEL -ZONE_CHANNEL = 'ias_zone' +ZONE_CHANNEL = ZONE = 'ias_zone' ELECTRICAL_MEASUREMENT_CHANNEL = 'electrical_measurement' POWER_CONFIGURATION_CHANNEL = 'power' EVENT_RELAY_CHANNEL = 'event_relay' From 9bb88a6143c2cab5c58fb4f176947bc74673be99 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 4 Apr 2019 17:43:18 +0200 Subject: [PATCH 411/605] Fix ingress routing with / (#22728) --- homeassistant/components/hassio/ingress.py | 2 +- tests/components/hassio/test_ingress.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 9b62bb89c94f67..49e949c5789239 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -30,7 +30,7 @@ class HassIOIngress(HomeAssistantView): """Hass.io view to handle base part.""" name = "api:hassio:ingress" - url = "/api/hassio_ingress/{token}/{path:.+}" + url = "/api/hassio_ingress/{token}/{path:.*}" requires_auth = False def __init__(self, host: str, websession: aiohttp.ClientSession): diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 0dda69b2b17b6e..343068375decdd 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -8,7 +8,8 @@ @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_get( hassio_client, build_type, aioclient_mock): @@ -40,7 +41,8 @@ async def test_ingress_request_get( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_post( hassio_client, build_type, aioclient_mock): @@ -72,7 +74,8 @@ async def test_ingress_request_post( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_put( hassio_client, build_type, aioclient_mock): @@ -104,7 +107,8 @@ async def test_ingress_request_put( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_delete( hassio_client, build_type, aioclient_mock): From 07d739c14e21d09b157ff0fcc7418308dd8ad5a8 Mon Sep 17 00:00:00 2001 From: Markus Ressel Date: Thu, 4 Apr 2019 19:18:54 +0200 Subject: [PATCH 412/605] Add N26 component (#22684) * upgraded n26 dependency removed card_id config parameter (unnecessary) get_token is now a public method inside n26 dependency * Add manifest.json --- .coveragerc | 1 + homeassistant/components/n26/__init__.py | 153 +++++++++++++ homeassistant/components/n26/const.py | 7 + homeassistant/components/n26/manifest.json | 10 + homeassistant/components/n26/sensor.py | 248 +++++++++++++++++++++ homeassistant/components/n26/switch.py | 64 ++++++ requirements_all.txt | 3 + 7 files changed, 486 insertions(+) create mode 100644 homeassistant/components/n26/__init__.py create mode 100644 homeassistant/components/n26/const.py create mode 100644 homeassistant/components/n26/manifest.json create mode 100644 homeassistant/components/n26/sensor.py create mode 100644 homeassistant/components/n26/switch.py diff --git a/.coveragerc b/.coveragerc index f8d8b0fc5215ef..957b3402c46f9e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -366,6 +366,7 @@ omit = homeassistant/components/mystrom/binary_sensor.py homeassistant/components/mystrom/light.py homeassistant/components/mystrom/switch.py + homeassistant/components/n26/* homeassistant/components/nad/media_player.py homeassistant/components/nadtcp/media_player.py homeassistant/components/nanoleaf/light.py diff --git a/homeassistant/components/n26/__init__.py b/homeassistant/components/n26/__init__.py new file mode 100644 index 00000000000000..8f4ade9c87f6ea --- /dev/null +++ b/homeassistant/components/n26/__init__.py @@ -0,0 +1,153 @@ +"""Support for N26 bank accounts.""" +from datetime import datetime, timedelta, timezone +import logging + +import voluptuous as vol + +from homeassistant.const import ( + CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.discovery import load_platform +from homeassistant.util import Throttle + +from .const import DATA, DOMAIN + +REQUIREMENTS = ['n26==0.2.7'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_SCAN_INTERVAL = timedelta(minutes=30) + +# define configuration parameters +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SCAN_INTERVAL, + default=DEFAULT_SCAN_INTERVAL): cv.time_period, + }), +}, extra=vol.ALLOW_EXTRA) + +N26_COMPONENTS = [ + 'sensor', + 'switch' +] + + +def setup(hass, config): + """Set up N26 Component.""" + user = config[DOMAIN][CONF_USERNAME] + password = config[DOMAIN][CONF_PASSWORD] + + from n26 import api, config as api_config + api = api.Api(api_config.Config(user, password)) + + from requests import HTTPError + try: + api.get_token() + except HTTPError as err: + _LOGGER.error(str(err)) + return False + + api_data = N26Data(api) + api_data.update() + + hass.data[DOMAIN] = {} + hass.data[DOMAIN][DATA] = api_data + + # Load components for supported devices + for component in N26_COMPONENTS: + load_platform(hass, component, DOMAIN, {}, config) + + return True + + +def timestamp_ms_to_date(epoch_ms) -> datetime or None: + """Convert millisecond timestamp to datetime.""" + if epoch_ms: + return datetime.fromtimestamp(epoch_ms / 1000, timezone.utc) + + +class N26Data: + """Handle N26 API object and limit updates.""" + + def __init__(self, api): + """Initialize the data object.""" + self._api = api + + self._account_info = {} + self._balance = {} + self._limits = {} + self._account_statuses = {} + + self._cards = {} + self._spaces = {} + + @property + def api(self): + """Return N26 api client.""" + return self._api + + @property + def account_info(self): + """Return N26 account info.""" + return self._account_info + + @property + def balance(self): + """Return N26 account balance.""" + return self._balance + + @property + def limits(self): + """Return N26 account limits.""" + return self._limits + + @property + def account_statuses(self): + """Return N26 account statuses.""" + return self._account_statuses + + @property + def cards(self): + """Return N26 cards.""" + return self._cards + + def card(self, card_id: str, default: dict = None): + """Return a card by its id or the given default.""" + return next((card for card in self.cards if card["id"] == card_id), + default) + + @property + def spaces(self): + """Return N26 spaces.""" + return self._spaces + + def space(self, space_id: str, default: dict = None): + """Return a space by its id or the given default.""" + return next((space for space in self.spaces["spaces"] + if space["id"] == space_id), default) + + @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) + def update_account(self): + """Get the latest account data from N26.""" + self._account_info = self._api.get_account_info() + self._balance = self._api.get_balance() + self._limits = self._api.get_account_limits() + self._account_statuses = self._api.get_account_statuses() + + @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) + def update_cards(self): + """Get the latest cards data from N26.""" + self._cards = self._api.get_cards() + + @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) + def update_spaces(self): + """Get the latest spaces data from N26.""" + self._spaces = self._api.get_spaces() + + def update(self): + """Get the latest data from N26.""" + self.update_account() + self.update_cards() + self.update_spaces() diff --git a/homeassistant/components/n26/const.py b/homeassistant/components/n26/const.py new file mode 100644 index 00000000000000..0a640d0f34e405 --- /dev/null +++ b/homeassistant/components/n26/const.py @@ -0,0 +1,7 @@ +"""Provides the constants needed for component.""" +DOMAIN = "n26" + +DATA = "data" + +CARD_STATE_ACTIVE = "M_ACTIVE" +CARD_STATE_BLOCKED = "M_DISABLED" diff --git a/homeassistant/components/n26/manifest.json b/homeassistant/components/n26/manifest.json new file mode 100644 index 00000000000000..b49932887d504b --- /dev/null +++ b/homeassistant/components/n26/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "n26", + "name": "N26", + "documentation": "https://www.home-assistant.io/components/n26", + "requirements": [ + "n26==0.2.7" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/n26/sensor.py b/homeassistant/components/n26/sensor.py new file mode 100644 index 00000000000000..682cd5dae68575 --- /dev/null +++ b/homeassistant/components/n26/sensor.py @@ -0,0 +1,248 @@ +"""Support for N26 bank account sensors.""" +import logging + +from homeassistant.helpers.entity import Entity + +from . import DEFAULT_SCAN_INTERVAL, DOMAIN, timestamp_ms_to_date +from .const import DATA + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['n26'] + +SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL + +ATTR_IBAN = "account" +ATTR_USABLE_BALANCE = "usable_balance" +ATTR_BANK_BALANCE = "bank_balance" + +ATTR_ACC_OWNER_TITLE = "owner_title" +ATTR_ACC_OWNER_FIRST_NAME = "owner_first_name" +ATTR_ACC_OWNER_LAST_NAME = "owner_last_name" +ATTR_ACC_OWNER_GENDER = "owner_gender" +ATTR_ACC_OWNER_BIRTH_DATE = "owner_birth_date" +ATTR_ACC_OWNER_EMAIL = "owner_email" +ATTR_ACC_OWNER_PHONE_NUMBER = "owner_phone_number" + +ICON_ACCOUNT = 'mdi:currency-eur' +ICON_CARD = 'mdi:credit-card' +ICON_SPACE = 'mdi:crop-square' + + +def setup_platform( + hass, config, add_entities, discovery_info=None): + """Set up the N26 sensor platform.""" + api_data = hass.data[DOMAIN][DATA] + + sensor_entities = [N26Account(api_data)] + + for card in api_data.cards: + sensor_entities.append(N26Card(api_data, card)) + + for space in api_data.spaces["spaces"]: + sensor_entities.append(N26Space(api_data, space)) + + add_entities(sensor_entities) + + +class N26Account(Entity): + """Sensor for a N26 balance account. + + A balance account contains an amount of money (=balance). The amount may + also be negative. + """ + + def __init__(self, api_data) -> None: + """Initialize a N26 balance account.""" + self._data = api_data + self._iban = self._data.balance["iban"] + + def update(self) -> None: + """Get the current balance and currency for the account.""" + self._data.update_account() + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return self._iban[-4:] + + @property + def name(self) -> str: + """Friendly name of the sensor.""" + return "n26_{}".format(self._iban[-4:]) + + @property + def state(self) -> float: + """Return the balance of the account as state.""" + if self._data.balance is None: + return None + + return self._data.balance.get("availableBalance") + + @property + def unit_of_measurement(self) -> str: + """Use the currency as unit of measurement.""" + if self._data.balance is None: + return None + + return self._data.balance.get("currency") + + @property + def device_state_attributes(self) -> dict: + """Additional attributes of the sensor.""" + attributes = { + ATTR_IBAN: self._data.balance.get("iban"), + ATTR_BANK_BALANCE: self._data.balance.get("bankBalance"), + ATTR_USABLE_BALANCE: self._data.balance.get("usableBalance"), + ATTR_ACC_OWNER_TITLE: self._data.account_info.get("title"), + ATTR_ACC_OWNER_FIRST_NAME: + self._data.account_info.get("kycFirstName"), + ATTR_ACC_OWNER_LAST_NAME: + self._data.account_info.get("kycLastName"), + ATTR_ACC_OWNER_GENDER: self._data.account_info.get("gender"), + ATTR_ACC_OWNER_BIRTH_DATE: timestamp_ms_to_date( + self._data.account_info.get("birthDate")), + ATTR_ACC_OWNER_EMAIL: self._data.account_info.get("email"), + ATTR_ACC_OWNER_PHONE_NUMBER: + self._data.account_info.get("mobilePhoneNumber"), + } + + for limit in self._data.limits: + limit_attr_name = "limit_{}".format(limit["limit"].lower()) + attributes[limit_attr_name] = limit["amount"] + + return attributes + + @property + def icon(self) -> str: + """Set the icon for the sensor.""" + return ICON_ACCOUNT + + +class N26Card(Entity): + """Sensor for a N26 card.""" + + def __init__(self, api_data, card) -> None: + """Initialize a N26 card.""" + self._data = api_data + self._account_name = api_data.balance["iban"][-4:] + self._card = card + + def update(self) -> None: + """Get the current balance and currency for the account.""" + self._data.update_cards() + self._card = self._data.card(self._card["id"], self._card) + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return self._card["id"] + + @property + def name(self) -> str: + """Friendly name of the sensor.""" + return "{}_card_{}".format( + self._account_name.lower(), self._card["id"]) + + @property + def state(self) -> float: + """Return the balance of the account as state.""" + return self._card["status"] + + @property + def device_state_attributes(self) -> dict: + """Additional attributes of the sensor.""" + attributes = { + "apple_pay_eligible": self._card.get("applePayEligible"), + "card_activated": timestamp_ms_to_date( + self._card.get("cardActivated")), + "card_product": self._card.get("cardProduct"), + "card_product_type": self._card.get("cardProductType"), + "card_settings_id": self._card.get("cardSettingsId"), + "card_Type": self._card.get("cardType"), + "design": self._card.get("design"), + "exceet_actual_delivery_date": + self._card.get("exceetActualDeliveryDate"), + "exceet_card_status": self._card.get("exceetCardStatus"), + "exceet_expected_delivery_date": + self._card.get("exceetExpectedDeliveryDate"), + "exceet_express_card_delivery": + self._card.get("exceetExpressCardDelivery"), + "exceet_express_card_delivery_email_sent": + self._card.get("exceetExpressCardDeliveryEmailSent"), + "exceet_express_card_delivery_tracking_id": + self._card.get("exceetExpressCardDeliveryTrackingId"), + "expiration_date": timestamp_ms_to_date( + self._card.get("expirationDate")), + "google_pay_eligible": self._card.get("googlePayEligible"), + "masked_pan": self._card.get("maskedPan"), + "membership": self._card.get("membership"), + "mpts_card": self._card.get("mptsCard"), + "pan": self._card.get("pan"), + "pin_defined": timestamp_ms_to_date(self._card.get("pinDefined")), + "username_on_card": self._card.get("usernameOnCard"), + } + return attributes + + @property + def icon(self) -> str: + """Set the icon for the sensor.""" + return ICON_CARD + + +class N26Space(Entity): + """Sensor for a N26 space.""" + + def __init__(self, api_data, space) -> None: + """Initialize a N26 space.""" + self._data = api_data + self._space = space + + def update(self) -> None: + """Get the current balance and currency for the account.""" + self._data.update_spaces() + self._space = self._data.space(self._space["id"], self._space) + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return "space_{}".format(self._space["name"].lower()) + + @property + def name(self) -> str: + """Friendly name of the sensor.""" + return self._space["name"] + + @property + def state(self) -> float: + """Return the balance of the account as state.""" + return self._space["balance"]["availableBalance"] + + @property + def unit_of_measurement(self) -> str: + """Use the currency as unit of measurement.""" + return self._space["balance"]["currency"] + + @property + def device_state_attributes(self) -> dict: + """Additional attributes of the sensor.""" + goal_value = "" + if "goal" in self._space: + goal_value = self._space.get("goal").get("amount") + + attributes = { + "name": self._space.get("name"), + "goal": goal_value, + "background_image_url": self._space.get("backgroundImageUrl"), + "image_url": self._space.get("imageUrl"), + "is_card_attached": self._space.get("isCardAttached"), + "is_hidden_from_balance": self._space.get("isHiddenFromBalance"), + "is_locked": self._space.get("isLocked"), + "is_primary": self._space.get("isPrimary"), + } + return attributes + + @property + def icon(self) -> str: + """Set the icon for the sensor.""" + return ICON_SPACE diff --git a/homeassistant/components/n26/switch.py b/homeassistant/components/n26/switch.py new file mode 100644 index 00000000000000..0e7455ea7030c8 --- /dev/null +++ b/homeassistant/components/n26/switch.py @@ -0,0 +1,64 @@ +"""Support for N26 switches.""" +import logging + +from homeassistant.components.switch import SwitchDevice + +from . import DEFAULT_SCAN_INTERVAL, DOMAIN +from .const import CARD_STATE_ACTIVE, CARD_STATE_BLOCKED, DATA + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['n26'] + +SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL + + +def setup_platform( + hass, config, add_entities, discovery_info=None): + """Set up the N26 switch platform.""" + api_data = hass.data[DOMAIN][DATA] + + switch_entities = [] + for card in api_data.cards: + switch_entities.append(N26CardSwitch(api_data, card)) + + add_entities(switch_entities) + + +class N26CardSwitch(SwitchDevice): + """Representation of a N26 card block/unblock switch.""" + + def __init__(self, api_data, card: dict): + """Initialize the N26 card block/unblock switch.""" + self._data = api_data + self._card = card + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return self._card["id"] + + @property + def name(self) -> str: + """Friendly name of the sensor.""" + return "card_{}".format(self._card["id"]) + + @property + def is_on(self): + """Return true if switch is on.""" + return self._card["status"] == CARD_STATE_ACTIVE + + def turn_on(self, **kwargs): + """Block the card.""" + self._data.api.unblock_card(self._card["id"]) + self._card["status"] = CARD_STATE_ACTIVE + + def turn_off(self, **kwargs): + """Unblock the card.""" + self._data.api.block_card(self._card["id"]) + self._card["status"] = CARD_STATE_BLOCKED + + def update(self): + """Update the switch state.""" + self._data.update_cards() + self._card = self._data.card(self._card["id"], self._card) diff --git a/requirements_all.txt b/requirements_all.txt index 69430c8cf98fb9..052f8990c206e9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -725,6 +725,9 @@ mycroftapi==2.0 # homeassistant.components.usps myusps==1.3.2 +# homeassistant.components.n26 +n26==0.2.7 + # homeassistant.components.nad.media_player nad_receiver==0.0.11 From 0438dffe25b1850e60ae365adab32539190f3b7a Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 4 Apr 2019 13:43:21 -0600 Subject: [PATCH 413/605] Bump aioambient to 0.2.0 (#22736) --- homeassistant/components/ambient_station/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 2383d0119457b4..ff94e4fd5e5e62 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -20,7 +20,7 @@ ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE, TYPE_BINARY_SENSOR, TYPE_SENSOR) -REQUIREMENTS = ['aioambient==0.1.3'] +REQUIREMENTS = ['aioambient==0.2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 052f8990c206e9..e32e951295748b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -97,7 +97,7 @@ abodepy==0.15.0 afsapi==0.0.4 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.2.0 # homeassistant.components.asuswrt aioasuswrt==1.1.21 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e08255c246d7fd..c6838f2dd1962f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -36,7 +36,7 @@ PyTransportNSW==0.1.1 YesssSMS==0.2.3 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.2.0 # homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 From 96adbfdc369c71a3612386285ad6f81f98874a2e Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 4 Apr 2019 13:50:10 -0600 Subject: [PATCH 414/605] Fix incorrect "Unavailable" Ambient sensors (#22734) * Fix incorrect "Unavailable" Ambient sensors * Removed unnecessary cast --- homeassistant/components/ambient_station/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index ff94e4fd5e5e62..cb0a2067d9f170 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -417,9 +417,8 @@ def __init__( @property def available(self): """Return True if entity is available.""" - return bool( - self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get( - self._sensor_type)) + return self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get( + self._sensor_type) is not None @property def device_info(self): From b9ec623ad905fd8b1526c0e7c5367abf4a91be71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Osb=C3=A4ck?= Date: Thu, 4 Apr 2019 23:19:29 +0200 Subject: [PATCH 415/605] Bump pywebpush to latest 1.9.2 (#22737) --- homeassistant/components/html5/notify.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py index f7b220ff1387a1..fa7bf660b79f59 100644 --- a/homeassistant/components/html5/notify.py +++ b/homeassistant/components/html5/notify.py @@ -24,7 +24,7 @@ ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['pywebpush==1.6.0'] +REQUIREMENTS = ['pywebpush==1.9.2'] DEPENDENCIES = ['frontend'] diff --git a/requirements_all.txt b/requirements_all.txt index e32e951295748b..1351a640008363 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1472,7 +1472,7 @@ pyvizio==0.0.4 pyvlx==0.2.10 # homeassistant.components.html5.notify -pywebpush==1.6.0 +pywebpush==1.9.2 # homeassistant.components.wemo pywemo==0.4.34 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c6838f2dd1962f..73fbae3aadb6c7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -265,7 +265,7 @@ pytradfri[async]==6.0.1 pyunifi==2.16 # homeassistant.components.html5.notify -pywebpush==1.6.0 +pywebpush==1.9.2 # homeassistant.components.rainmachine regenmaschine==1.4.0 From b50afec5f11e07352f40f9bdbbaf5bff7a6b11ff Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 5 Apr 2019 02:48:24 +0200 Subject: [PATCH 416/605] Support multiple deCONZ gateways (#22449) * Store gateways inside a dict in deconz domain * Make reachable events gateway specific * Gateway shall always exist * Adapt new device signalling to support multiple gateways * Services follow gateway master * Working on unload entry * Make unload and master handover work Improve tests for init * Fix config flow * Fix linting * Clean up init tests * Clean up hassio discovery to fit with the rest * Store gateways inside a dict in deconz domain * Make reachable events gateway specific * Gateway shall always exist * Adapt new device signalling to support multiple gateways * Services follow gateway master * Working on unload entry * Make unload and master handover work Improve tests for init * Fix config flow * Fix linting * Clean up init tests * Clean up hassio discovery to fit with the rest * Add support for services to specify bridgeid --- homeassistant/components/deconz/__init__.py | 96 +++++--- .../components/deconz/binary_sensor.py | 19 +- homeassistant/components/deconz/climate.py | 19 +- .../components/deconz/config_flow.py | 121 ++++------ homeassistant/components/deconz/const.py | 19 +- homeassistant/components/deconz/cover.py | 14 +- .../components/deconz/deconz_device.py | 5 +- homeassistant/components/deconz/gateway.py | 73 +++++- homeassistant/components/deconz/light.py | 22 +- homeassistant/components/deconz/scene.py | 12 +- homeassistant/components/deconz/sensor.py | 20 +- homeassistant/components/deconz/services.yaml | 8 +- homeassistant/components/deconz/switch.py | 13 +- tests/components/deconz/test_binary_sensor.py | 35 +-- tests/components/deconz/test_climate.py | 47 ++-- tests/components/deconz/test_config_flow.py | 73 ++---- tests/components/deconz/test_cover.py | 24 +- tests/components/deconz/test_init.py | 210 ++++++++++++------ tests/components/deconz/test_light.py | 49 ++-- tests/components/deconz/test_scene.py | 15 +- tests/components/deconz/test_sensor.py | 47 ++-- tests/components/deconz/test_switch.py | 28 +-- 22 files changed, 539 insertions(+), 430 deletions(-) diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index 8bdd946e2ef898..ff1ee2bf06e3e5 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -4,12 +4,14 @@ from homeassistant import config_entries from homeassistant.const import ( CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP) +from homeassistant.core import callback from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC # Loading the config flow file will register the flow -from .config_flow import configured_hosts -from .const import DEFAULT_PORT, DOMAIN, _LOGGER +from .config_flow import get_master_gateway +from .const import ( + CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, + CONF_MASTER_GATEWAY, DEFAULT_PORT, DOMAIN, _LOGGER) from .gateway import DeconzGateway REQUIREMENTS = ['pydeconz==54'] @@ -32,26 +34,27 @@ vol.Optional(SERVICE_ENTITY): cv.entity_id, vol.Optional(SERVICE_FIELD): cv.matches_regex('/.*'), vol.Required(SERVICE_DATA): dict, + vol.Optional(CONF_BRIDGEID): str }), cv.has_at_least_one_key(SERVICE_ENTITY, SERVICE_FIELD)) SERVICE_DEVICE_REFRESH = 'device_refresh' +SERVICE_DEVICE_REFRESCH_SCHEMA = vol.All(vol.Schema({ + vol.Optional(CONF_BRIDGEID): str +})) + async def async_setup(hass, config): """Load configuration for deCONZ component. Discovery has loaded the component if DOMAIN is not present in config. """ - if DOMAIN in config: - deconz_config = None - if CONF_HOST in config[DOMAIN]: - deconz_config = config[DOMAIN] - if deconz_config and not configured_hosts(hass): - hass.async_add_job(hass.config_entries.flow.async_init( - DOMAIN, - context={'source': config_entries.SOURCE_IMPORT}, - data=deconz_config - )) + if not hass.config_entries.async_entries(DOMAIN) and DOMAIN in config: + deconz_config = config[DOMAIN] + hass.async_add_job(hass.config_entries.flow.async_init( + DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, + data=deconz_config + )) return True @@ -61,26 +64,20 @@ async def async_setup_entry(hass, config_entry): Load config, group, light and sensor data for server information. Start websocket for push notification of state changes from deCONZ. """ - if DOMAIN in hass.data: - _LOGGER.error( - "Config entry failed since one deCONZ instance already exists") - return False + if DOMAIN not in hass.data: + hass.data[DOMAIN] = {} + + if not config_entry.options: + await async_populate_options(hass, config_entry) gateway = DeconzGateway(hass, config_entry) if not await gateway.async_setup(): return False - hass.data[DOMAIN] = gateway + hass.data[DOMAIN][gateway.bridgeid] = gateway - device_registry = await \ - hass.helpers.device_registry.async_get_registry() - device_registry.async_get_or_create( - config_entry_id=config_entry.entry_id, - connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)}, - identifiers={(DOMAIN, gateway.api.config.bridgeid)}, - manufacturer='Dresden Elektronik', model=gateway.api.config.modelid, - name=gateway.api.config.name, sw_version=gateway.api.config.swversion) + await gateway.async_update_device_registry() async def async_configure(call): """Set attribute of device in deCONZ. @@ -100,8 +97,11 @@ async def async_configure(call): """ field = call.data.get(SERVICE_FIELD, '') entity_id = call.data.get(SERVICE_ENTITY) - data = call.data.get(SERVICE_DATA) - gateway = hass.data[DOMAIN] + data = call.data[SERVICE_DATA] + + gateway = get_master_gateway(hass) + if CONF_BRIDGEID in call.data: + gateway = hass.data[DOMAIN][call.data[CONF_BRIDGEID]] if entity_id: try: @@ -117,7 +117,9 @@ async def async_configure(call): async def async_refresh_devices(call): """Refresh available devices from deCONZ.""" - gateway = hass.data[DOMAIN] + gateway = get_master_gateway(hass) + if CONF_BRIDGEID in call.data: + gateway = hass.data[DOMAIN][call.data[CONF_BRIDGEID]] groups = set(gateway.api.groups.keys()) lights = set(gateway.api.lights.keys()) @@ -151,7 +153,8 @@ async def async_refresh_devices(call): ) hass.services.async_register( - DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices) + DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices, + schema=SERVICE_DEVICE_REFRESCH_SCHEMA) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown) return True @@ -159,7 +162,34 @@ async def async_refresh_devices(call): async def async_unload_entry(hass, config_entry): """Unload deCONZ config entry.""" - gateway = hass.data.pop(DOMAIN) - hass.services.async_remove(DOMAIN, SERVICE_DECONZ) - hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH) + gateway = hass.data[DOMAIN].pop(config_entry.data[CONF_BRIDGEID]) + + if not hass.data[DOMAIN]: + hass.services.async_remove(DOMAIN, SERVICE_DECONZ) + hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH) + elif gateway.master: + await async_populate_options(hass, config_entry) + new_master_gateway = next(iter(hass.data[DOMAIN].values())) + await async_populate_options(hass, new_master_gateway.config_entry) + return await gateway.async_reset() + + +@callback +async def async_populate_options(hass, config_entry): + """Populate default options for gateway. + + Called by setup_entry and unload_entry. + Makes sure there is always one master available. + """ + master = not get_master_gateway(hass) + + options = { + CONF_MASTER_GATEWAY: master, + CONF_ALLOW_CLIP_SENSOR: config_entry.data.get( + CONF_ALLOW_CLIP_SENSOR, False), + CONF_ALLOW_DECONZ_GROUPS: config_entry.data.get( + CONF_ALLOW_DECONZ_GROUPS, True) + } + + hass.config_entries.async_update_entry(config_entry, options=options) diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index 2b0c2037248042..70de1fd7cf44ba 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -4,10 +4,9 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import ( - ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN, - NEW_SENSOR) +from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -24,22 +23,26 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ binary sensor.""" - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_sensor(sensors): """Add binary sensor from deCONZ.""" from pydeconz.sensor import DECONZ_BINARY_SENSOR entities = [] - allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) + for sensor in sensors: + if sensor.type in DECONZ_BINARY_SENSOR and \ - not (not allow_clip_sensor and sensor.type.startswith('CLIP')): + not (not gateway.allow_clip_sensor and + sensor.type.startswith('CLIP')): + entities.append(DeconzBinarySensor(sensor, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor)) async_add_sensor(gateway.api.sensors.values()) diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index 1f39b8705c723a..c4327d3c497fec 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -7,10 +7,9 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import ( - ATTR_OFFSET, ATTR_VALVE, CONF_ALLOW_CLIP_SENSOR, - DOMAIN as DECONZ_DOMAIN, NEW_SENSOR) +from .const import ATTR_OFFSET, ATTR_VALVE, NEW_SENSOR from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -20,22 +19,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities): Thermostats are based on the same device class as sensors in deCONZ. """ - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_climate(sensors): """Add climate devices from deCONZ.""" from pydeconz.sensor import THERMOSTAT entities = [] - allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) + for sensor in sensors: + if sensor.type in THERMOSTAT and \ - not (not allow_clip_sensor and sensor.type.startswith('CLIP')): + not (not gateway.allow_clip_sensor and + sensor.type.startswith('CLIP')): + entities.append(DeconzThermostat(sensor, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_SENSOR, async_add_climate)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_SENSOR), async_add_climate)) async_add_climate(gateway.api.sensors.values()) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 38849fb37b3ff3..1ecfee7ada5cb2 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -9,10 +9,7 @@ from homeassistant.core import callback from homeassistant.helpers import aiohttp_client -from .const import ( - CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, - DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT, - DOMAIN) +from .const import CONF_BRIDGEID, DEFAULT_PORT, DOMAIN @callback @@ -22,6 +19,14 @@ def configured_hosts(hass): in hass.config_entries.async_entries(DOMAIN)) +@callback +def get_master_gateway(hass): + """Return a bool telling if this is the master gateway.""" + for gateway in hass.data[DOMAIN].values(): + if gateway.master: + return gateway + + @config_entries.HANDLERS.register(DOMAIN) class DeconzFlowHandler(config_entries.ConfigFlow): """Handle a deCONZ config flow.""" @@ -39,16 +44,12 @@ def __init__(self): async def async_step_user(self, user_input=None): """Handle a deCONZ config flow start. - Only allows one instance to be set up. If only one bridge is found go to link step. If more than one bridge is found let user choose bridge to link. If no bridge is found allow user to manually input configuration. """ from pydeconz.utils import async_discovery - if configured_hosts(self.hass): - return self.async_abort(reason='one_instance_only') - if user_input is not None: for bridge in self.bridges: if bridge[CONF_HOST] == user_input[CONF_HOST]: @@ -99,9 +100,6 @@ async def async_step_link(self, user_input=None): errors = {} if user_input is not None: - if configured_hosts(self.hass): - return self.async_abort(reason='one_instance_only') - session = aiohttp_client.async_get_clientsession(self.hass) try: @@ -114,51 +112,32 @@ async def async_step_link(self, user_input=None): else: self.deconz_config[CONF_API_KEY] = api_key - return await self.async_step_options() + return await self._create_entry() return self.async_show_form( step_id='link', errors=errors, ) - async def async_step_options(self, user_input=None): - """Extra options for deCONZ. - - CONF_CLIP_SENSOR -- Allow user to choose if they want clip sensors. - CONF_DECONZ_GROUPS -- Allow user to choose if they want deCONZ groups. - """ + async def _create_entry(self): + """Create entry for gateway.""" from pydeconz.utils import async_get_bridgeid - if user_input is not None: - self.deconz_config[CONF_ALLOW_CLIP_SENSOR] = \ - user_input[CONF_ALLOW_CLIP_SENSOR] - self.deconz_config[CONF_ALLOW_DECONZ_GROUPS] = \ - user_input[CONF_ALLOW_DECONZ_GROUPS] - - if CONF_BRIDGEID not in self.deconz_config: - session = aiohttp_client.async_get_clientsession(self.hass) - try: - with async_timeout.timeout(10): - self.deconz_config[CONF_BRIDGEID] = \ - await async_get_bridgeid( - session, **self.deconz_config) - - except asyncio.TimeoutError: - return self.async_abort(reason='no_bridges') - - return self.async_create_entry( - title='deCONZ-' + self.deconz_config[CONF_BRIDGEID], - data=self.deconz_config - ) + if CONF_BRIDGEID not in self.deconz_config: + session = aiohttp_client.async_get_clientsession(self.hass) - return self.async_show_form( - step_id='options', - data_schema=vol.Schema({ - vol.Optional(CONF_ALLOW_CLIP_SENSOR, - default=DEFAULT_ALLOW_CLIP_SENSOR): bool, - vol.Optional(CONF_ALLOW_DECONZ_GROUPS, - default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, - }), + try: + with async_timeout.timeout(10): + self.deconz_config[CONF_BRIDGEID] = \ + await async_get_bridgeid( + session, **self.deconz_config) + + except asyncio.TimeoutError: + return self.async_abort(reason='no_bridges') + + return self.async_create_entry( + title='deCONZ-' + self.deconz_config[CONF_BRIDGEID], + data=self.deconz_config ) async def async_step_discovery(self, discovery_info): @@ -166,10 +145,14 @@ async def async_step_discovery(self, discovery_info): This flow is triggered by the discovery component. """ - deconz_config = {} - deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST) - deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT) - deconz_config[CONF_BRIDGEID] = discovery_info.get('serial') + deconz_config = { + CONF_HOST: discovery_info[CONF_HOST], + CONF_PORT: discovery_info[CONF_PORT], + CONF_BRIDGEID: discovery_info['serial'] + } + + if deconz_config[CONF_HOST] in configured_hosts(self.hass): + return self.async_abort(reason='one_instance_only') return await self.async_step_import(deconz_config) @@ -186,16 +169,11 @@ async def async_step_import(self, import_config): Otherwise we will delegate to `link` step which will ask user to link the bridge. """ - if configured_hosts(self.hass): - return self.async_abort(reason='one_instance_only') - self.deconz_config = import_config if CONF_API_KEY not in import_config: return await self.async_step_link() - user_input = {CONF_ALLOW_CLIP_SENSOR: True, - CONF_ALLOW_DECONZ_GROUPS: True} - return await self.async_step_options(user_input=user_input) + return await self._create_entry() async def async_step_hassio(self, user_input=None): """Prepare configuration for a Hass.io deCONZ bridge. @@ -212,29 +190,18 @@ async def async_step_hassio(self, user_input=None): async def async_step_hassio_confirm(self, user_input=None): """Confirm a Hass.io discovery.""" if user_input is not None: - data = self._hassio_discovery - - return self.async_create_entry( - title=data['addon'], data={ - CONF_HOST: data[CONF_HOST], - CONF_PORT: data[CONF_PORT], - CONF_BRIDGEID: data['serial'], - CONF_API_KEY: data[CONF_API_KEY], - CONF_ALLOW_CLIP_SENSOR: - user_input[CONF_ALLOW_CLIP_SENSOR], - CONF_ALLOW_DECONZ_GROUPS: - user_input[CONF_ALLOW_DECONZ_GROUPS], - }) + self.deconz_config = { + CONF_HOST: self._hassio_discovery[CONF_HOST], + CONF_PORT: self._hassio_discovery[CONF_PORT], + CONF_BRIDGEID: self._hassio_discovery['serial'], + CONF_API_KEY: self._hassio_discovery[CONF_API_KEY] + } + + return await self._create_entry() return self.async_show_form( step_id='hassio_confirm', description_placeholders={ 'addon': self._hassio_discovery['addon'] - }, - data_schema=vol.Schema({ - vol.Optional(CONF_ALLOW_CLIP_SENSOR, - default=DEFAULT_ALLOW_CLIP_SENSOR): bool, - vol.Optional(CONF_ALLOW_DECONZ_GROUPS, - default=DEFAULT_ALLOW_DECONZ_GROUPS): bool, - }) + } ) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index b26fddd914755c..bf0f5884073c52 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -12,22 +12,21 @@ CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' CONF_BRIDGEID = 'bridgeid' +CONF_MASTER_GATEWAY = 'master' SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'cover', 'light', 'scene', 'sensor', 'switch'] -DECONZ_REACHABLE = 'deconz_reachable' - -NEW_GROUP = 'deconz_new_group' -NEW_LIGHT = 'deconz_new_light' -NEW_SCENE = 'deconz_new_scene' -NEW_SENSOR = 'deconz_new_sensor' +NEW_GROUP = 'group' +NEW_LIGHT = 'light' +NEW_SCENE = 'scene' +NEW_SENSOR = 'sensor' NEW_DEVICE = { - 'group': NEW_GROUP, - 'light': NEW_LIGHT, - 'scene': NEW_SCENE, - 'sensor': NEW_SENSOR + NEW_GROUP: 'deconz_new_group_{}', + NEW_LIGHT: 'deconz_new_light_{}', + NEW_SCENE: 'deconz_new_scene_{}', + NEW_SENSOR: 'deconz_new_sensor_{}' } ATTR_DARK = 'dark' diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index fda4fe4309c375..903c1160eb8226 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -5,9 +5,9 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import ( - COVER_TYPES, DAMPERS, DOMAIN as DECONZ_DOMAIN, NEW_LIGHT, WINDOW_COVERS) +from .const import COVER_TYPES, DAMPERS, NEW_LIGHT, WINDOW_COVERS from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -25,22 +25,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities): Covers are based on same device class as lights in deCONZ. """ - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_cover(lights): """Add cover from deCONZ.""" entities = [] + for light in lights: + if light.type in COVER_TYPES: if light.modelid in ZIGBEE_SPEC: entities.append(DeconzCoverZigbeeSpec(light, gateway)) + else: entities.append(DeconzCover(light, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_LIGHT, async_add_cover)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_LIGHT), async_add_cover)) async_add_cover(gateway.api.lights.values()) diff --git a/homeassistant/components/deconz/deconz_device.py b/homeassistant/components/deconz/deconz_device.py index bfcbd158b9f457..0c5cbeef1fba92 100644 --- a/homeassistant/components/deconz/deconz_device.py +++ b/homeassistant/components/deconz/deconz_device.py @@ -4,7 +4,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from .const import DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN +from .const import DOMAIN as DECONZ_DOMAIN class DeconzDevice(Entity): @@ -21,7 +21,8 @@ async def async_added_to_hass(self): self._device.register_async_callback(self.async_update_callback) self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id self.unsub_dispatcher = async_dispatcher_connect( - self.hass, DECONZ_REACHABLE, self.async_update_callback) + self.hass, self.gateway.event_reachable, + self.async_update_callback) async def async_will_remove_from_hass(self) -> None: """Disconnect device object when removed.""" diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 11fb247a6f456e..4d9e15039024c2 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -6,16 +6,23 @@ from homeassistant.const import CONF_EVENT, CONF_HOST, CONF_ID from homeassistant.core import EventOrigin, callback from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send) from homeassistant.util import slugify from .const import ( - _LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, NEW_DEVICE, NEW_SENSOR, - SUPPORTED_PLATFORMS) + _LOGGER, CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID, + CONF_MASTER_GATEWAY, DOMAIN, NEW_DEVICE, NEW_SENSOR, SUPPORTED_PLATFORMS) from .errors import AuthenticationRequired, CannotConnect +@callback +def get_gateway_from_config_entry(hass, config_entry): + """Return gateway with a matching bridge id.""" + return hass.data[DOMAIN][config_entry.data[CONF_BRIDGEID]] + + class DeconzGateway: """Manages a single deCONZ gateway.""" @@ -30,6 +37,40 @@ def __init__(self, hass, config_entry): self.events = [] self.listeners = [] + @property + def bridgeid(self) -> str: + """Return the unique identifier of the gateway.""" + return self.config_entry.data[CONF_BRIDGEID] + + @property + def master(self) -> bool: + """Gateway which is used with deCONZ services without defining id.""" + return self.config_entry.options[CONF_MASTER_GATEWAY] + + @property + def allow_clip_sensor(self) -> bool: + """Allow loading clip sensor from gateway.""" + return self.config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) + + @property + def allow_deconz_groups(self) -> bool: + """Allow loading deCONZ groups from gateway.""" + return self.config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True) + + async def async_update_device_registry(self): + """Update device registry.""" + device_registry = await \ + self.hass.helpers.device_registry.async_get_registry() + device_registry.async_get_or_create( + config_entry_id=self.config_entry.entry_id, + connections={(CONNECTION_NETWORK_MAC, self.api.config.mac)}, + identifiers={(DOMAIN, self.api.config.bridgeid)}, + manufacturer='Dresden Elektronik', + model=self.api.config.modelid, + name=self.api.config.name, + sw_version=self.api.config.swversion + ) + async def async_setup(self): """Set up a deCONZ gateway.""" hass = self.hass @@ -52,9 +93,9 @@ async def async_setup(self): hass.config_entries.async_forward_entry_setup( self.config_entry, component)) - self.listeners.append( - async_dispatcher_connect( - hass, NEW_SENSOR, self.async_add_remote)) + self.listeners.append(async_dispatcher_connect( + hass, self.async_event_new_device(NEW_SENSOR), + self.async_add_remote)) self.async_add_remote(self.api.sensors.values()) @@ -62,29 +103,39 @@ async def async_setup(self): return True + @property + def event_reachable(self): + """Gateway specific event to signal a change in connection status.""" + return 'deconz_reachable_{}'.format(self.bridgeid) + @callback def async_connection_status_callback(self, available): """Handle signals of gateway connection status.""" self.available = available - async_dispatcher_send( - self.hass, DECONZ_REACHABLE, {'state': True, 'attr': 'reachable'}) + async_dispatcher_send(self.hass, self.event_reachable, + {'state': True, 'attr': 'reachable'}) + + @callback + def async_event_new_device(self, device_type): + """Gateway specific event to signal new device.""" + return NEW_DEVICE[device_type].format(self.bridgeid) @callback def async_add_device_callback(self, device_type, device): """Handle event of new device creation in deCONZ.""" if not isinstance(device, list): device = [device] - async_dispatcher_send(self.hass, NEW_DEVICE[device_type], device) + async_dispatcher_send( + self.hass, self.async_event_new_device(device_type), device) @callback def async_add_remote(self, sensors): """Set up remote from deCONZ.""" from pydeconz.sensor import SWITCH as DECONZ_REMOTE - allow_clip_sensor = self.config_entry.data.get( - CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_REMOTE and \ - not (not allow_clip_sensor and sensor.type.startswith('CLIP')): + not (not self.allow_clip_sensor and + sensor.type.startswith('CLIP')): self.events.append(DeconzEvent(self.hass, sensor)) @callback diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 3b63da8d9f8a18..b5a2b075f75bdf 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -8,10 +8,9 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.util.color as color_util -from .const import ( - CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DECONZ_DOMAIN, COVER_TYPES, NEW_GROUP, - NEW_LIGHT, SWITCH_TYPES) +from .const import COVER_TYPES, NEW_GROUP, NEW_LIGHT, SWITCH_TYPES from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -24,32 +23,35 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ lights and groups from a config entry.""" - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_light(lights): """Add light from deCONZ.""" entities = [] + for light in lights: if light.type not in COVER_TYPES + SWITCH_TYPES: entities.append(DeconzLight(light, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_LIGHT, async_add_light)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_LIGHT), async_add_light)) @callback def async_add_group(groups): """Add group from deCONZ.""" entities = [] - allow_group = config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True) + for group in groups: - if group.lights and allow_group: + if group.lights and gateway.allow_deconz_groups: entities.append(DeconzLight(group, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_GROUP, async_add_group)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_GROUP), async_add_group)) async_add_light(gateway.api.lights.values()) async_add_group(gateway.api.groups.values()) diff --git a/homeassistant/components/deconz/scene.py b/homeassistant/components/deconz/scene.py index 22b4c47f2ab8b1..1ae1e079daa065 100644 --- a/homeassistant/components/deconz/scene.py +++ b/homeassistant/components/deconz/scene.py @@ -3,7 +3,8 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import DOMAIN as DECONZ_DOMAIN, NEW_SCENE +from .const import NEW_SCENE +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -16,17 +17,20 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up scenes for deCONZ component.""" - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_scene(scenes): """Add scene from deCONZ.""" entities = [] + for scene in scenes: entities.append(DeconzScene(scene, gateway)) + async_add_entities(entities) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_SCENE, async_add_scene)) + + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_SCENE), async_add_scene)) async_add_scene(gateway.api.scenes.values()) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index e6b033906e76a6..7c3109e1f5961f 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -5,10 +5,9 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import slugify -from .const import ( - ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN, - NEW_SENSOR) +from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -25,7 +24,7 @@ async def async_setup_platform( async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ sensors.""" - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_sensor(sensors): @@ -33,19 +32,24 @@ def async_add_sensor(sensors): from pydeconz.sensor import ( DECONZ_SENSOR, SWITCH as DECONZ_REMOTE) entities = [] - allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) + for sensor in sensors: + if sensor.type in DECONZ_SENSOR and \ - not (not allow_clip_sensor and sensor.type.startswith('CLIP')): + not (not gateway.allow_clip_sensor and + sensor.type.startswith('CLIP')): + if sensor.type in DECONZ_REMOTE: if sensor.battery: entities.append(DeconzBattery(sensor, gateway)) + else: entities.append(DeconzSensor(sensor, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor)) async_add_sensor(gateway.api.sensors.values()) diff --git a/homeassistant/components/deconz/services.yaml b/homeassistant/components/deconz/services.yaml index cde7ac79f4c741..a39bbc01ea1fda 100644 --- a/homeassistant/components/deconz/services.yaml +++ b/homeassistant/components/deconz/services.yaml @@ -13,6 +13,12 @@ configure: data: description: Data is a json object with what data you want to alter. example: '{"on": true}' + bridgeid: + description: (Optional) Bridgeid is a string unique for each deCONZ hardware. It can be found as part of the integration name. + example: '00212EFFFF012345' device_refresh: - description: Refresh device lists from deCONZ. \ No newline at end of file + description: Refresh device lists from deCONZ. + bridgeid: + description: (Optional) Bridgeid is a string unique for each deCONZ hardware. It can be found as part of the integration name. + example: '00212EFFFF012345' \ No newline at end of file diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index 56d37d504cbe87..b9f959766fc2c2 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -3,8 +3,9 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import DOMAIN as DECONZ_DOMAIN, NEW_LIGHT, POWER_PLUGS, SIRENS +from .const import NEW_LIGHT, POWER_PLUGS, SIRENS from .deconz_device import DeconzDevice +from .gateway import get_gateway_from_config_entry DEPENDENCIES = ['deconz'] @@ -20,21 +21,25 @@ async def async_setup_entry(hass, config_entry, async_add_entities): Switches are based same device class as lights in deCONZ. """ - gateway = hass.data[DECONZ_DOMAIN] + gateway = get_gateway_from_config_entry(hass, config_entry) @callback def async_add_switch(lights): """Add switch from deCONZ.""" entities = [] + for light in lights: + if light.type in POWER_PLUGS: entities.append(DeconzPowerPlug(light, gateway)) + elif light.type in SIRENS: entities.append(DeconzSiren(light, gateway)) + async_add_entities(entities, True) - gateway.listeners.append( - async_dispatcher_connect(hass, NEW_LIGHT, async_add_switch)) + gateway.listeners.append(async_dispatcher_connect( + hass, gateway.async_event_new_device(NEW_LIGHT), async_add_switch)) async_add_switch(gateway.api.lights.values()) diff --git a/tests/components/deconz/test_binary_sensor.py b/tests/components/deconz/test_binary_sensor.py index ba39afa0e88fac..1aee53f43c2746 100644 --- a/tests/components/deconz/test_binary_sensor.py +++ b/tests/components/deconz/test_binary_sensor.py @@ -54,7 +54,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -64,6 +64,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): config_entry, 'binary_sensor') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -79,56 +80,56 @@ async def test_platform_manually_configured(hass): async def test_no_binary_sensors(hass): """Test that no sensors in deconz results in no sensor entities.""" data = {} - await setup_gateway(hass, data) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, data) + assert len(hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids) == 0 assert len(hass.states.async_all()) == 0 async def test_binary_sensors(hass): """Test successful creation of binary sensor entities.""" data = {"sensors": SENSOR} - await setup_gateway(hass, data) - assert "binary_sensor.sensor_1_name" in \ - hass.data[deconz.DOMAIN].deconz_ids - assert "binary_sensor.sensor_2_name" not in \ - hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, data) + assert "binary_sensor.sensor_1_name" in gateway.deconz_ids + assert "binary_sensor.sensor_2_name" not in gateway.deconz_ids assert len(hass.states.async_all()) == 1 - hass.data[deconz.DOMAIN].api.sensors['1'].async_update( + hass.data[deconz.DOMAIN][gateway.bridgeid].api.sensors['1'].async_update( {'state': {'on': False}}) async def test_add_new_sensor(hass): """Test successful creation of sensor entities.""" data = {} - await setup_gateway(hass, data) + gateway = await setup_gateway(hass, data) sensor = Mock() sensor.name = 'name' sensor.type = 'ZHAPresence' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert "binary_sensor.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "binary_sensor.name" in gateway.deconz_ids async def test_do_not_allow_clip_sensor(hass): """Test that clip sensors can be ignored.""" data = {} - await setup_gateway(hass, data, allow_clip_sensor=False) + gateway = await setup_gateway(hass, data, allow_clip_sensor=False) sensor = Mock() sensor.name = 'name' sensor.type = 'CLIPPresence' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + assert len(gateway.deconz_ids) == 0 async def test_unload_switch(hass): """Test that it works to unload switch entities.""" data = {"sensors": SENSOR} - await setup_gateway(hass, data) + gateway = await setup_gateway(hass, data) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 0 diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py index 953bb776419887..a5f4d2bb79b6e2 100644 --- a/tests/components/deconz/test_climate.py +++ b/tests/components/deconz/test_climate.py @@ -65,7 +65,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(hass.loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -75,6 +75,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): config_entry, 'climate') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -89,26 +90,26 @@ async def test_platform_manually_configured(hass): async def test_no_sensors(hass): """Test that no sensors in deconz results in no climate entities.""" - await setup_gateway(hass, {}) - assert not hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert not hass.states.async_all() async def test_climate_devices(hass): """Test successful creation of sensor entities.""" - await setup_gateway(hass, {"sensors": SENSOR}) - assert "climate.climate_1_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_2_name" not in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"sensors": SENSOR}) + assert "climate.climate_1_name" in gateway.deconz_ids + assert "sensor.sensor_2_name" not in gateway.deconz_ids assert len(hass.states.async_all()) == 1 - hass.data[deconz.DOMAIN].api.sensors['1'].async_update( + gateway.api.sensors['1'].async_update( {'state': {'on': False}}) await hass.services.async_call( 'climate', 'turn_on', {'entity_id': 'climate.climate_1_name'}, blocking=True ) - hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + gateway.api.session.put.assert_called_with( 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', data='{"mode": "auto"}' ) @@ -117,7 +118,7 @@ async def test_climate_devices(hass): 'climate', 'turn_off', {'entity_id': 'climate.climate_1_name'}, blocking=True ) - hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + gateway.api.session.put.assert_called_with( 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', data='{"mode": "off"}' ) @@ -127,18 +128,18 @@ async def test_climate_devices(hass): {'entity_id': 'climate.climate_1_name', 'temperature': 20}, blocking=True ) - hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + gateway.api.session.put.assert_called_with( 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', data='{"heatsetpoint": 2000.0}' ) - assert len(hass.data[deconz.DOMAIN].api.session.put.mock_calls) == 3 + assert len(gateway.api.session.put.mock_calls) == 3 async def test_verify_state_update(hass): """Test that state update properly.""" - await setup_gateway(hass, {"sensors": SENSOR}) - assert "climate.climate_1_name" in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"sensors": SENSOR}) + assert "climate.climate_1_name" in gateway.deconz_ids thermostat = hass.states.get('climate.climate_1_name') assert thermostat.state == 'on' @@ -150,7 +151,7 @@ async def test_verify_state_update(hass): "id": "1", "config": {"on": False} } - hass.data[deconz.DOMAIN].api.async_event_handler(state_update) + gateway.api.async_event_handler(state_update) await hass.async_block_till_done() assert len(hass.states.async_all()) == 1 @@ -161,32 +162,34 @@ async def test_verify_state_update(hass): async def test_add_new_climate_device(hass): """Test successful creation of climate entities.""" - await setup_gateway(hass, {}) + gateway = await setup_gateway(hass, {}) sensor = Mock() sensor.name = 'name' sensor.type = 'ZHAThermostat' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert "climate.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "climate.name" in gateway.deconz_ids async def test_do_not_allow_clipsensor(hass): """Test that clip sensors can be ignored.""" - await setup_gateway(hass, {}, allow_clip_sensor=False) + gateway = await setup_gateway(hass, {}, allow_clip_sensor=False) sensor = Mock() sensor.name = 'name' sensor.type = 'CLIPThermostat' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + assert len(gateway.deconz_ids) == 0 async def test_unload_sensor(hass): """Test that it works to unload sensor entities.""" - await setup_gateway(hass, {"sensors": SENSOR}) + gateway = await setup_gateway(hass, {"sensors": SENSOR}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 0 diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 863e4e93fc5873..09510b136c463b 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -22,10 +22,7 @@ async def test_flow_works(hass, aioclient_mock): flow.hass = hass await flow.async_step_user() - await flow.async_step_link(user_input={}) - - result = await flow.async_step_options( - user_input={'allow_clip_sensor': True, 'allow_deconz_groups': True}) + result = await flow.async_step_link(user_input={}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' @@ -33,25 +30,10 @@ async def test_flow_works(hass, aioclient_mock): 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF', - 'allow_clip_sensor': True, - 'allow_deconz_groups': True + 'api_key': '1234567890ABCDEF' } -async def test_flow_already_registered_bridge(hass): - """Test config flow don't allow more than one bridge to be registered.""" - MockConfigEntry(domain='deconz', data={ - 'host': '1.2.3.4' - }).add_to_hass(hass) - - flow = config_flow.DeconzFlowHandler() - flow.hass = hass - - result = await flow.async_step_user() - assert result['type'] == 'abort' - - async def test_flow_bridge_discovery_fails(hass, aioclient_mock): """Test config flow works when discovery fails.""" flow = config_flow.DeconzFlowHandler() @@ -153,24 +135,6 @@ async def test_link_no_api_key(hass): assert result['errors'] == {'base': 'no_key'} -async def test_link_already_registered_bridge(hass): - """Test that link verifies to only allow one config entry to complete. - - This is possible with discovery which will allow the user to complete - a second config entry and then complete the discovered config entry. - """ - MockConfigEntry(domain='deconz', data={ - 'host': '1.2.3.4' - }).add_to_hass(hass) - - flow = config_flow.DeconzFlowHandler() - flow.hass = hass - flow.deconz_config = {'host': '1.2.3.4', 'port': 80} - - result = await flow.async_step_link(user_input={}) - assert result['type'] == 'abort' - - async def test_bridge_discovery(hass): """Test a bridge being discovered.""" flow = config_flow.DeconzFlowHandler() @@ -197,6 +161,7 @@ async def test_bridge_discovery_already_configured(hass): result = await flow.async_step_discovery({ 'host': '1.2.3.4', + 'port': 80, 'serial': 'id' }) @@ -234,14 +199,12 @@ async def test_import_with_api_key(hass): 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF', - 'allow_clip_sensor': True, - 'allow_deconz_groups': True + 'api_key': '1234567890ABCDEF' } -async def test_options(hass, aioclient_mock): - """Test that options work and that bridgeid can be requested.""" +async def test_create_entry(hass, aioclient_mock): + """Test that _create_entry work and that bridgeid can be requested.""" aioclient_mock.get('http://1.2.3.4:80/api/1234567890ABCDEF/config', json={"bridgeid": "id"}, headers={'content-type': 'application/json'}) @@ -252,8 +215,7 @@ async def test_options(hass, aioclient_mock): 'port': 80, 'api_key': '1234567890ABCDEF'} - result = await flow.async_step_options( - user_input={'allow_clip_sensor': False, 'allow_deconz_groups': False}) + result = await flow._create_entry() assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' @@ -261,9 +223,7 @@ async def test_options(hass, aioclient_mock): 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF', - 'allow_clip_sensor': False, - 'allow_deconz_groups': False + 'api_key': '1234567890ABCDEF' } @@ -286,8 +246,8 @@ async def test_hassio_confirm(hass): data={ 'addon': 'Mock Addon', 'host': 'mock-deconz', - 'port': 8080, - 'serial': 'aa:bb', + 'port': 80, + 'serial': 'id', 'api_key': '1234567890ABCDEF', }, context={'source': 'hassio'} @@ -299,18 +259,13 @@ async def test_hassio_confirm(hass): } result = await hass.config_entries.flow.async_configure( - result['flow_id'], { - 'allow_clip_sensor': True, - 'allow_deconz_groups': True, - } + result['flow_id'], user_input={} ) assert result['type'] == 'create_entry' assert result['result'].data == { 'host': 'mock-deconz', - 'port': 8080, - 'bridgeid': 'aa:bb', - 'api_key': '1234567890ABCDEF', - 'allow_clip_sensor': True, - 'allow_deconz_groups': True, + 'port': 80, + 'bridgeid': 'id', + 'api_key': '1234567890ABCDEF' } diff --git a/tests/components/deconz/test_cover.py b/tests/components/deconz/test_cover.py index b021bcb8d512f8..73e3d41195855f 100644 --- a/tests/components/deconz/test_cover.py +++ b/tests/components/deconz/test_cover.py @@ -61,7 +61,7 @@ async def setup_gateway(hass, data): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -70,6 +70,7 @@ async def setup_gateway(hass, data): await hass.config_entries.async_forward_entry_setup(config_entry, 'cover') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -84,8 +85,8 @@ async def test_platform_manually_configured(hass): async def test_no_covers(hass): """Test that no cover entities are created.""" - await setup_gateway(hass, {}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert len(hass.states.async_all()) == 0 @@ -93,8 +94,8 @@ async def test_cover(hass): """Test that all supported cover entities are created.""" with patch('pydeconz.DeconzSession.async_put_state', return_value=mock_coro(True)): - await setup_gateway(hass, {"lights": SUPPORTED_COVERS}) - assert "cover.cover_1_name" in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"lights": SUPPORTED_COVERS}) + assert "cover.cover_1_name" in gateway.deconz_ids assert len(SUPPORTED_COVERS) == len(COVER_TYPES) assert len(hass.states.async_all()) == 3 @@ -102,7 +103,7 @@ async def test_cover(hass): assert cover_1 is not None assert cover_1.state == 'closed' - hass.data[deconz.DOMAIN].api.lights['1'].async_update({}) + gateway.api.lights['1'].async_update({}) await hass.services.async_call('cover', 'open_cover', { 'entity_id': 'cover.cover_1_name' @@ -122,14 +123,15 @@ async def test_cover(hass): async def test_add_new_cover(hass): """Test successful creation of cover entity.""" data = {} - await setup_gateway(hass, data) + gateway = await setup_gateway(hass, data) cover = Mock() cover.name = 'name' cover.type = "Level controllable output" cover.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_light', [cover]) + async_dispatcher_send( + hass, gateway.async_event_new_device('light'), [cover]) await hass.async_block_till_done() - assert "cover.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "cover.name" in gateway.deconz_ids async def test_unsupported_cover(hass): @@ -140,8 +142,8 @@ async def test_unsupported_cover(hass): async def test_unload_cover(hass): """Test that it works to unload switch entities.""" - await setup_gateway(hass, {"lights": SUPPORTED_COVERS}) + gateway = await setup_gateway(hass, {"lights": SUPPORTED_COVERS}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 1 diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index e0afadccc81581..da37f4a9652006 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -11,56 +11,62 @@ from tests.common import mock_coro, MockConfigEntry - -CONFIG = { - "config": { - "bridgeid": "0123456789ABCDEF", - "mac": "12:34:56:78:90:ab", - "modelid": "deCONZ", - "name": "Phoscon", - "swversion": "2.05.35" - } -} +ENTRY1_HOST = '1.2.3.4' +ENTRY1_PORT = 80 +ENTRY1_API_KEY = '1234567890ABCDEF' +ENTRY1_BRIDGEID = '12345ABC' + +ENTRY2_HOST = '2.3.4.5' +ENTRY2_PORT = 80 +ENTRY2_API_KEY = '1234567890ABCDEF' +ENTRY2_BRIDGEID = '23456DEF' + + +async def setup_entry(hass, entry): + """Test that setup entry works.""" + with patch.object(deconz.DeconzGateway, 'async_setup', + return_value=mock_coro(True)), \ + patch.object(deconz.DeconzGateway, 'async_update_device_registry', + return_value=mock_coro(True)): + assert await deconz.async_setup_entry(hass, entry) is True async def test_config_with_host_passed_to_config_entry(hass): """Test that configured options for a host are loaded via config entry.""" - with patch.object(hass, 'config_entries') as mock_config_entries, \ - patch.object(deconz, 'configured_hosts', return_value=[]): + with patch.object(hass.config_entries, 'flow') as mock_config_flow: assert await async_setup_component(hass, deconz.DOMAIN, { deconz.DOMAIN: { - deconz.CONF_HOST: '1.2.3.4', - deconz.CONF_PORT: 80 + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT } }) is True # Import flow started - assert len(mock_config_entries.flow.mock_calls) == 2 + assert len(mock_config_flow.mock_calls) == 2 async def test_config_without_host_not_passed_to_config_entry(hass): """Test that a configuration without a host does not initiate an import.""" - with patch.object(hass, 'config_entries') as mock_config_entries, \ - patch.object(deconz, 'configured_hosts', return_value=[]): + MockConfigEntry(domain=deconz.DOMAIN, data={}).add_to_hass(hass) + with patch.object(hass.config_entries, 'flow') as mock_config_flow: assert await async_setup_component(hass, deconz.DOMAIN, { deconz.DOMAIN: {} }) is True # No flow started - assert len(mock_config_entries.flow.mock_calls) == 0 + assert len(mock_config_flow.mock_calls) == 0 -async def test_config_already_registered_not_passed_to_config_entry(hass): +async def test_config_import_entry_fails_when_entries_exist(hass): """Test that an already registered host does not initiate an import.""" - with patch.object(hass, 'config_entries') as mock_config_entries, \ - patch.object(deconz, 'configured_hosts', - return_value=['1.2.3.4']): + MockConfigEntry(domain=deconz.DOMAIN, data={}).add_to_hass(hass) + with patch.object(hass.config_entries, 'flow') as mock_config_flow: assert await async_setup_component(hass, deconz.DOMAIN, { deconz.DOMAIN: { - deconz.CONF_HOST: '1.2.3.4', - deconz.CONF_PORT: 80 + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT } }) is True # No flow started - assert len(mock_config_entries.flow.mock_calls) == 0 + assert len(mock_config_flow.mock_calls) == 0 async def test_config_discovery(hass): @@ -71,16 +77,14 @@ async def test_config_discovery(hass): assert len(mock_config_entries.flow.mock_calls) == 0 -async def test_setup_entry_already_registered_bridge(hass): - """Test setup entry doesn't allow more than one instance of deCONZ.""" - hass.data[deconz.DOMAIN] = True - assert await deconz.async_setup_entry(hass, {}) is False - - async def test_setup_entry_fails(hass): """Test setup entry fails if deCONZ is not available.""" entry = Mock() - entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} + entry.data = { + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY + } with patch('pydeconz.DeconzSession.async_load_parameters', side_effect=Exception): await deconz.async_setup_entry(hass, entry) @@ -89,61 +93,121 @@ async def test_setup_entry_fails(hass): async def test_setup_entry_no_available_bridge(hass): """Test setup entry fails if deCONZ is not available.""" entry = Mock() - entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} - with patch( - 'pydeconz.DeconzSession.async_load_parameters', - side_effect=asyncio.TimeoutError - ), pytest.raises(ConfigEntryNotReady): + entry.data = { + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY + } + with patch('pydeconz.DeconzSession.async_load_parameters', + side_effect=asyncio.TimeoutError),\ + pytest.raises(ConfigEntryNotReady): await deconz.async_setup_entry(hass, entry) async def test_setup_entry_successful(hass): """Test setup entry is successful.""" entry = MockConfigEntry(domain=deconz.DOMAIN, data={ - 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID }) entry.add_to_hass(hass) - mock_registry = Mock() - with patch.object(deconz, 'DeconzGateway') as mock_gateway, \ - patch('homeassistant.helpers.device_registry.async_get_registry', - return_value=mock_coro(mock_registry)): - mock_gateway.return_value.async_setup.return_value = mock_coro(True) - assert await deconz.async_setup_entry(hass, entry) is True - assert hass.data[deconz.DOMAIN] + + await setup_entry(hass, entry) + + assert ENTRY1_BRIDGEID in hass.data[deconz.DOMAIN] + assert hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].master + + +async def test_setup_entry_multiple_gateways(hass): + """Test setup entry is successful with multiple gateways.""" + entry = MockConfigEntry(domain=deconz.DOMAIN, data={ + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID + }) + entry.add_to_hass(hass) + + entry2 = MockConfigEntry(domain=deconz.DOMAIN, data={ + deconz.CONF_HOST: ENTRY2_HOST, + deconz.CONF_PORT: ENTRY2_PORT, + deconz.CONF_API_KEY: ENTRY2_API_KEY, + deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID + }) + entry2.add_to_hass(hass) + + await setup_entry(hass, entry) + await setup_entry(hass, entry2) + + assert ENTRY1_BRIDGEID in hass.data[deconz.DOMAIN] + assert hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].master + assert ENTRY2_BRIDGEID in hass.data[deconz.DOMAIN] + assert not hass.data[deconz.DOMAIN][ENTRY2_BRIDGEID].master async def test_unload_entry(hass): """Test being able to unload an entry.""" entry = MockConfigEntry(domain=deconz.DOMAIN, data={ - 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID }) entry.add_to_hass(hass) - mock_registry = Mock() - with patch.object(deconz, 'DeconzGateway') as mock_gateway, \ - patch('homeassistant.helpers.device_registry.async_get_registry', - return_value=mock_coro(mock_registry)): - mock_gateway.return_value.async_setup.return_value = mock_coro(True) - assert await deconz.async_setup_entry(hass, entry) is True - mock_gateway.return_value.async_reset.return_value = mock_coro(True) - assert await deconz.async_unload_entry(hass, entry) - assert deconz.DOMAIN not in hass.data + await setup_entry(hass, entry) + + with patch.object(deconz.DeconzGateway, 'async_reset', + return_value=mock_coro(True)): + assert await deconz.async_unload_entry(hass, entry) + + assert not hass.data[deconz.DOMAIN] + + +async def test_unload_entry_multiple_gateways(hass): + """Test being able to unload an entry and master gateway gets moved.""" + entry = MockConfigEntry(domain=deconz.DOMAIN, data={ + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID + }) + entry.add_to_hass(hass) + + entry2 = MockConfigEntry(domain=deconz.DOMAIN, data={ + deconz.CONF_HOST: ENTRY2_HOST, + deconz.CONF_PORT: ENTRY2_PORT, + deconz.CONF_API_KEY: ENTRY2_API_KEY, + deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID + }) + entry2.add_to_hass(hass) + + await setup_entry(hass, entry) + await setup_entry(hass, entry2) + + with patch.object(deconz.DeconzGateway, 'async_reset', + return_value=mock_coro(True)): + assert await deconz.async_unload_entry(hass, entry) + + assert ENTRY2_BRIDGEID in hass.data[deconz.DOMAIN] + assert hass.data[deconz.DOMAIN][ENTRY2_BRIDGEID].master async def test_service_configure(hass): """Test that service invokes pydeconz with the correct path and data.""" entry = MockConfigEntry(domain=deconz.DOMAIN, data={ - 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID }) entry.add_to_hass(hass) - mock_registry = Mock() - with patch.object(deconz, 'DeconzGateway') as mock_gateway, \ - patch('homeassistant.helpers.device_registry.async_get_registry', - return_value=mock_coro(mock_registry)): - mock_gateway.return_value.async_setup.return_value = mock_coro(True) - assert await deconz.async_setup_entry(hass, entry) is True - hass.data[deconz.DOMAIN].deconz_ids = { + await setup_entry(hass, entry) + + hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].deconz_ids = { 'light.test': '/light/1' } data = {'on': True, 'attr1': 10, 'attr2': 20} @@ -191,25 +255,23 @@ async def test_service_configure(hass): async def test_service_refresh_devices(hass): """Test that service can refresh devices.""" entry = MockConfigEntry(domain=deconz.DOMAIN, data={ - 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' + deconz.CONF_HOST: ENTRY1_HOST, + deconz.CONF_PORT: ENTRY1_PORT, + deconz.CONF_API_KEY: ENTRY1_API_KEY, + deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID }) entry.add_to_hass(hass) - mock_registry = Mock() - with patch.object(deconz, 'DeconzGateway') as mock_gateway, \ - patch('homeassistant.helpers.device_registry.async_get_registry', - return_value=mock_coro(mock_registry)): - mock_gateway.return_value.async_setup.return_value = mock_coro(True) - assert await deconz.async_setup_entry(hass, entry) is True + await setup_entry(hass, entry) - with patch.object(hass.data[deconz.DOMAIN].api, 'async_load_parameters', - return_value=mock_coro(True)): + with patch('pydeconz.DeconzSession.async_load_parameters', + return_value=mock_coro(True)): await hass.services.async_call( 'deconz', 'device_refresh', service_data={}) await hass.async_block_till_done() - with patch.object(hass.data[deconz.DOMAIN].api, 'async_load_parameters', - return_value=mock_coro(False)): + with patch('pydeconz.DeconzSession.async_load_parameters', + return_value=mock_coro(False)): await hass.services.async_call( 'deconz', 'device_refresh', service_data={}) await hass.async_block_till_done() diff --git a/tests/components/deconz/test_light.py b/tests/components/deconz/test_light.py index 49c3f280d8a344..d9f6927fe2c13a 100644 --- a/tests/components/deconz/test_light.py +++ b/tests/components/deconz/test_light.py @@ -87,7 +87,7 @@ async def setup_gateway(hass, data, allow_deconz_groups=True): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -96,6 +96,7 @@ async def setup_gateway(hass, data, allow_deconz_groups=True): await hass.config_entries.async_forward_entry_setup(config_entry, 'light') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -110,8 +111,8 @@ async def test_platform_manually_configured(hass): async def test_no_lights_or_groups(hass): """Test that no lights or groups entities are created.""" - await setup_gateway(hass, {}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert len(hass.states.async_all()) == 0 @@ -119,11 +120,12 @@ async def test_lights_and_groups(hass): """Test that lights or groups entities are created.""" with patch('pydeconz.DeconzSession.async_put_state', return_value=mock_coro(True)): - await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP}) - assert "light.light_1_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "light.light_2_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "light.group_1_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "light.group_2_name" not in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway( + hass, {"lights": LIGHT, "groups": GROUP}) + assert "light.light_1_name" in gateway.deconz_ids + assert "light.light_2_name" in gateway.deconz_ids + assert "light.group_1_name" in gateway.deconz_ids + assert "light.group_2_name" not in gateway.deconz_ids assert len(hass.states.async_all()) == 4 lamp_1 = hass.states.get('light.light_1_name') @@ -137,7 +139,7 @@ async def test_lights_and_groups(hass): assert light_2.state == 'on' assert light_2.attributes['color_temp'] == 2500 - hass.data[deconz.DOMAIN].api.lights['1'].async_update({}) + gateway.api.lights['1'].async_update({}) await hass.services.async_call('light', 'turn_on', { 'entity_id': 'light.light_1_name', @@ -166,49 +168,52 @@ async def test_lights_and_groups(hass): async def test_add_new_light(hass): """Test successful creation of light entities.""" - await setup_gateway(hass, {}) + gateway = await setup_gateway(hass, {}) light = Mock() light.name = 'name' light.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_light', [light]) + async_dispatcher_send( + hass, gateway.async_event_new_device('light'), [light]) await hass.async_block_till_done() - assert "light.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "light.name" in gateway.deconz_ids async def test_add_new_group(hass): """Test successful creation of group entities.""" - await setup_gateway(hass, {}) + gateway = await setup_gateway(hass, {}) group = Mock() group.name = 'name' group.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_group', [group]) + async_dispatcher_send( + hass, gateway.async_event_new_device('group'), [group]) await hass.async_block_till_done() - assert "light.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "light.name" in gateway.deconz_ids async def test_do_not_add_deconz_groups(hass): """Test that clip sensors can be ignored.""" - await setup_gateway(hass, {}, allow_deconz_groups=False) + gateway = await setup_gateway(hass, {}, allow_deconz_groups=False) group = Mock() group.name = 'name' group.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_group', [group]) + async_dispatcher_send( + hass, gateway.async_event_new_device('group'), [group]) await hass.async_block_till_done() - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + assert len(gateway.deconz_ids) == 0 async def test_no_switch(hass): """Test that a switch doesn't get created as a light entity.""" - await setup_gateway(hass, {"lights": SWITCH}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {"lights": SWITCH}) + assert len(gateway.deconz_ids) == 0 assert len(hass.states.async_all()) == 0 async def test_unload_light(hass): """Test that it works to unload switch entities.""" - await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP}) + gateway = await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() # Group.all_lights will not be removed assert len(hass.states.async_all()) == 1 diff --git a/tests/components/deconz/test_scene.py b/tests/components/deconz/test_scene.py index 963f1064b35659..0feac24f22a7e7 100644 --- a/tests/components/deconz/test_scene.py +++ b/tests/components/deconz/test_scene.py @@ -47,7 +47,7 @@ async def setup_gateway(hass, data): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -56,6 +56,7 @@ async def setup_gateway(hass, data): await hass.config_entries.async_forward_entry_setup(config_entry, 'scene') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -70,8 +71,8 @@ async def test_platform_manually_configured(hass): async def test_no_scenes(hass): """Test that scenes can be loaded without scenes being available.""" - await setup_gateway(hass, {}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert len(hass.states.async_all()) == 0 @@ -79,8 +80,8 @@ async def test_scenes(hass): """Test that scenes works.""" with patch('pydeconz.DeconzSession.async_put_state', return_value=mock_coro(True)): - await setup_gateway(hass, {"groups": GROUP}) - assert "scene.group_1_name_scene_1" in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"groups": GROUP}) + assert "scene.group_1_name_scene_1" in gateway.deconz_ids assert len(hass.states.async_all()) == 1 await hass.services.async_call('scene', 'turn_on', { @@ -90,8 +91,8 @@ async def test_scenes(hass): async def test_unload_scene(hass): """Test that it works to unload scene entities.""" - await setup_gateway(hass, {"groups": GROUP}) + gateway = await setup_gateway(hass, {"groups": GROUP}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 0 diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index f5cfbe2c183513..41bb7b362f5f86 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -91,7 +91,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -101,6 +101,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): config_entry, 'sensor') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -115,58 +116,56 @@ async def test_platform_manually_configured(hass): async def test_no_sensors(hass): """Test that no sensors in deconz results in no sensor entities.""" - await setup_gateway(hass, {}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert len(hass.states.async_all()) == 0 async def test_sensors(hass): """Test successful creation of sensor entities.""" - await setup_gateway(hass, {"sensors": SENSOR}) - assert "sensor.sensor_1_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_2_name" not in hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_3_name" not in hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_3_name_battery_level" not in \ - hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_4_name" not in hass.data[deconz.DOMAIN].deconz_ids - assert "sensor.sensor_4_name_battery_level" in \ - hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"sensors": SENSOR}) + assert "sensor.sensor_1_name" in gateway.deconz_ids + assert "sensor.sensor_2_name" not in gateway.deconz_ids + assert "sensor.sensor_3_name" not in gateway.deconz_ids + assert "sensor.sensor_3_name_battery_level" not in gateway.deconz_ids + assert "sensor.sensor_4_name" not in gateway.deconz_ids + assert "sensor.sensor_4_name_battery_level" in gateway.deconz_ids assert len(hass.states.async_all()) == 5 - hass.data[deconz.DOMAIN].api.sensors['1'].async_update( - {'state': {'on': False}}) - hass.data[deconz.DOMAIN].api.sensors['4'].async_update( - {'config': {'battery': 75}}) + gateway.api.sensors['1'].async_update({'state': {'on': False}}) + gateway.api.sensors['4'].async_update({'config': {'battery': 75}}) async def test_add_new_sensor(hass): """Test successful creation of sensor entities.""" - await setup_gateway(hass, {}) + gateway = await setup_gateway(hass, {}) sensor = Mock() sensor.name = 'name' sensor.type = 'ZHATemperature' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert "sensor.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "sensor.name" in gateway.deconz_ids async def test_do_not_allow_clipsensor(hass): """Test that clip sensors can be ignored.""" - await setup_gateway(hass, {}, allow_clip_sensor=False) + gateway = await setup_gateway(hass, {}, allow_clip_sensor=False) sensor = Mock() sensor.name = 'name' sensor.type = 'CLIPTemperature' sensor.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + async_dispatcher_send( + hass, gateway.async_event_new_device('sensor'), [sensor]) await hass.async_block_till_done() - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + assert len(gateway.deconz_ids) == 0 async def test_unload_sensor(hass): """Test that it works to unload sensor entities.""" - await setup_gateway(hass, {"sensors": SENSOR}) + gateway = await setup_gateway(hass, {"sensors": SENSOR}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 0 diff --git a/tests/components/deconz/test_switch.py b/tests/components/deconz/test_switch.py index 245be27961d0a7..e05362953a1b36 100644 --- a/tests/components/deconz/test_switch.py +++ b/tests/components/deconz/test_switch.py @@ -65,7 +65,7 @@ async def setup_gateway(hass, data): gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) gateway.api.config = Mock() - hass.data[deconz.DOMAIN] = gateway + hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway} with patch('pydeconz.DeconzSession.async_get_state', return_value=mock_coro(data)): @@ -74,6 +74,7 @@ async def setup_gateway(hass, data): await hass.config_entries.async_forward_entry_setup(config_entry, 'switch') # To flush out the service call to update the group await hass.async_block_till_done() + return gateway async def test_platform_manually_configured(hass): @@ -88,8 +89,8 @@ async def test_platform_manually_configured(hass): async def test_no_switches(hass): """Test that no switch entities are created.""" - await setup_gateway(hass, {}) - assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + gateway = await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids assert len(hass.states.async_all()) == 0 @@ -97,10 +98,10 @@ async def test_switches(hass): """Test that all supported switch entities are created.""" with patch('pydeconz.DeconzSession.async_put_state', return_value=mock_coro(True)): - await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES}) - assert "switch.switch_1_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "switch.switch_2_name" in hass.data[deconz.DOMAIN].deconz_ids - assert "switch.switch_3_name" in hass.data[deconz.DOMAIN].deconz_ids + gateway = await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES}) + assert "switch.switch_1_name" in gateway.deconz_ids + assert "switch.switch_2_name" in gateway.deconz_ids + assert "switch.switch_3_name" in gateway.deconz_ids assert len(SUPPORTED_SWITCHES) == len(SWITCH_TYPES) assert len(hass.states.async_all()) == 4 @@ -111,7 +112,7 @@ async def test_switches(hass): assert switch_3 is not None assert switch_3.state == 'on' - hass.data[deconz.DOMAIN].api.lights['1'].async_update({}) + gateway.api.lights['1'].async_update({}) await hass.services.async_call('switch', 'turn_on', { 'entity_id': 'switch.switch_1_name' @@ -130,14 +131,15 @@ async def test_switches(hass): async def test_add_new_switch(hass): """Test successful creation of switch entity.""" - await setup_gateway(hass, {}) + gateway = await setup_gateway(hass, {}) switch = Mock() switch.name = 'name' switch.type = "Smart plug" switch.register_async_callback = Mock() - async_dispatcher_send(hass, 'deconz_new_light', [switch]) + async_dispatcher_send( + hass, gateway.async_event_new_device('light'), [switch]) await hass.async_block_till_done() - assert "switch.name" in hass.data[deconz.DOMAIN].deconz_ids + assert "switch.name" in gateway.deconz_ids async def test_unsupported_switch(hass): @@ -148,8 +150,8 @@ async def test_unsupported_switch(hass): async def test_unload_switch(hass): """Test that it works to unload switch entities.""" - await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES}) + gateway = await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES}) - await hass.data[deconz.DOMAIN].async_reset() + await gateway.async_reset() assert len(hass.states.async_all()) == 1 From 6996fec809b759e7075677c1c6e3f4e4a9394e7b Mon Sep 17 00:00:00 2001 From: Wolfgang Malgadey Date: Fri, 5 Apr 2019 02:52:06 +0200 Subject: [PATCH 417/605] Fix tado turn on off (#22291) * fix for turn on and off, with new pyTado missing blank line * removed, because can't push * uploaded the file through github again --- homeassistant/components/tado/__init__.py | 5 +++++ homeassistant/components/tado/climate.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 6808729685eb11..8d3f541972e50c 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -121,3 +121,8 @@ def set_zone_overlay(self, zone_id, mode, temperature=None, duration=None): """Wrap for setZoneOverlay(..).""" self.tado.setZoneOverlay(zone_id, mode, temperature, duration) self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg + + def set_zone_off(self, zone_id, mode): + """Set a zone to off.""" + self.tado.setZoneOverlay(zone_id, mode, None, None, 'HEATING', 'OFF') + self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 56c670184b5b44..90d5f076974f77 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -363,7 +363,7 @@ def _control_heating(self): if self._current_operation == CONST_MODE_OFF: _LOGGER.info("Switching mytado.com to OFF for zone %s", self.zone_name) - self._store.set_zone_overlay(self.zone_id, CONST_OVERLAY_MANUAL) + self._store.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL) self._overlay_mode = self._current_operation return From eadc1e037af1d7098ac450f53ac936c0745d4eb5 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Fri, 5 Apr 2019 03:37:59 +0200 Subject: [PATCH 418/605] add device class signal strength (#22738) --- homeassistant/components/sensor/__init__.py | 5 +++-- homeassistant/const.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index e11ace9749c996..8996311830024a 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -7,8 +7,8 @@ from homeassistant.const import ( DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, - DEVICE_CLASS_POWER, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, - DEVICE_CLASS_TIMESTAMP) + DEVICE_CLASS_POWER, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP) from homeassistant.helpers.config_validation import ( # noqa PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE) from homeassistant.helpers.entity_component import EntityComponent @@ -24,6 +24,7 @@ DEVICE_CLASS_BATTERY, # % of battery that is left DEVICE_CLASS_HUMIDITY, # % of humidity in the air DEVICE_CLASS_ILLUMINANCE, # current light level (lx/lm) + DEVICE_CLASS_SIGNAL_STRENGTH, # signal strength (dB/dBm) DEVICE_CLASS_TEMPERATURE, # temperature (C/F) DEVICE_CLASS_TIMESTAMP, # timestamp (ISO8601) DEVICE_CLASS_PRESSURE, # pressure (hPa/mbar) diff --git a/homeassistant/const.py b/homeassistant/const.py index 295a73a0e6c474..e4f1ac95af49d7 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -180,6 +180,7 @@ DEVICE_CLASS_BATTERY = 'battery' DEVICE_CLASS_HUMIDITY = 'humidity' DEVICE_CLASS_ILLUMINANCE = 'illuminance' +DEVICE_CLASS_SIGNAL_STRENGTH = 'signal_strength' DEVICE_CLASS_TEMPERATURE = 'temperature' DEVICE_CLASS_TIMESTAMP = 'timestamp' DEVICE_CLASS_PRESSURE = 'pressure' From b130c433c94873c65701074a237d2af86a63a0fe Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 4 Apr 2019 20:50:07 -0700 Subject: [PATCH 419/605] Update pywebpush version in manifest.json Missed during #22737 --- homeassistant/components/html5/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/html5/manifest.json b/homeassistant/components/html5/manifest.json index 98b2834be7f804..81e4072e62994a 100644 --- a/homeassistant/components/html5/manifest.json +++ b/homeassistant/components/html5/manifest.json @@ -3,7 +3,7 @@ "name": "HTML5 Notifications", "documentation": "https://www.home-assistant.io/components/html5", "requirements": [ - "pywebpush==1.6.0" + "pywebpush==1.9.2" ], "dependencies": [], "codeowners": [ From be579b783aba180a7997b4cd8751c77a4a9b3d0c Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Thu, 4 Apr 2019 21:24:55 -0700 Subject: [PATCH 420/605] Update PR template requirements to point to the manifest (#22751) ## Description: Update the PR template to point requirements to the new manifest requirements. **Related issue (if applicable):** relates to #22700 **Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** https://github.com/home-assistant/developers.home-assistant/pull/214 ## Example entry for `configuration.yaml` (if applicable): ```yaml ``` ## Checklist: - [ ] The code change is tested and works locally. - [ ] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [ ] There is no commented out code in this PR. If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [ ] New files were added to `.coveragerc`. If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 --- .github/PULL_REQUEST_TEMPLATE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ecdbddf5b5de0f..9b3ca90db2f374 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -23,7 +23,8 @@ If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]). + - [ ] There is a manifest with all fields filled out correctly ([example][ex-manifest]). + - [ ] New dependencies have been added to `requirements` in the manifest ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. - [ ] New files were added to `.coveragerc`. @@ -31,5 +32,6 @@ If the code communicates with devices, web services, or third-party tools: If the code does not interact with devices: - [ ] Tests have been added to verify that the new code works. -[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 +[ex-manifest]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json +[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json#L5 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 From 6c5f0b74349dd84762b76b454aeb45cfd0f6e7f2 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 4 Apr 2019 21:27:18 -0700 Subject: [PATCH 421/605] It doesnt count as a fail if you catch it within 2 minutes --- .github/PULL_REQUEST_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9b3ca90db2f374..ebebf487275407 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -23,7 +23,7 @@ If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) If the code communicates with devices, web services, or third-party tools: - - [ ] There is a manifest with all fields filled out correctly ([example][ex-manifest]). + - [ ] [_The manifest file_][manifest-docs] has all fields filled out correctly ([example][ex-manifest]). - [ ] New dependencies have been added to `requirements` in the manifest ([example][ex-requir]). - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]). - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. @@ -35,3 +35,4 @@ If the code does not interact with devices: [ex-manifest]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json#L5 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 +[manifest-docs]: https://developers.home-assistant.io/docs/en/development_checklist.html#_the-manifest-file_ From d15eedc0fb3f5ea023c710407b7af6ca24f799a6 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Thu, 4 Apr 2019 21:29:29 -0700 Subject: [PATCH 422/605] Generate requirements_* from manifests (#22718) ## Description: Generate requirements_* from manifests (if present). If not, fallback to the current approach of reading `REQUIREMENTS` from the module attribute. I disabled exploring the children of the `homeassistant.components.*` packages since that will just add a dependency (from the manifest) due to each of the python files in the package. Just having one for the top level package should be sufficient. **Related issue (if applicable):** relates to #22700 ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L14 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 Co-authored-by: Jason Hu --- requirements_all.txt | 834 +++++++++++++++----------------- requirements_test_all.txt | 110 ++--- script/gen_requirements_all.py | 65 +-- script/manifest/requirements.py | 22 + 4 files changed, 511 insertions(+), 520 deletions(-) create mode 100644 script/manifest/requirements.py diff --git a/requirements_all.txt b/requirements_all.txt index 1351a640008363..7d154cd2ed7135 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -20,13 +20,13 @@ voluptuous-serialize==2.1.0 # homeassistant.components.nuimo_controller --only-binary=all nuimo==0.1.0 -# homeassistant.components.dht.sensor +# homeassistant.components.dht # Adafruit-DHT==1.4.0 -# homeassistant.components.sht31.sensor +# homeassistant.components.sht31 Adafruit-GPIO==1.0.3 -# homeassistant.components.sht31.sensor +# homeassistant.components.sht31 Adafruit-SHT31==1.0.2 # homeassistant.components.bbb_gpio @@ -35,16 +35,16 @@ Adafruit-SHT31==1.0.2 # homeassistant.components.homekit HAP-python==2.4.2 -# homeassistant.components.mastodon.notify +# homeassistant.components.mastodon Mastodon.py==1.3.1 -# homeassistant.components.github.sensor +# homeassistant.components.github PyGithub==1.43.5 # homeassistant.components.isy994 PyISY==1.1.1 -# homeassistant.components.mvglive.sensor +# homeassistant.components.mvglive PyMVGLive==1.1.4 # homeassistant.components.arduino @@ -57,13 +57,13 @@ PyNaCl==1.3.0 # homeassistant.auth.mfa_modules.totp PyQRCode==1.2.1 -# homeassistant.components.rmvtransport.sensor +# homeassistant.components.rmvtransport PyRMVtransport==0.1.3 -# homeassistant.components.switchbot.switch +# homeassistant.components.switchbot # PySwitchbot==0.5 -# homeassistant.components.transport_nsw.sensor +# homeassistant.components.transport_nsw PyTransportNSW==0.1.1 # homeassistant.components.xiaomi_aqara @@ -75,40 +75,40 @@ PyXiaomiGateway==0.12.2 # homeassistant.components.remember_the_milk RtmAPI==0.7.0 -# homeassistant.components.travisci.sensor +# homeassistant.components.travisci TravisPy==0.3.5 -# homeassistant.components.twitter.notify +# homeassistant.components.twitter TwitterAPI==2.5.9 -# homeassistant.components.tof.sensor +# homeassistant.components.tof # VL53L1X2==0.1.5 -# homeassistant.components.waze_travel_time.sensor +# homeassistant.components.waze_travel_time WazeRouteCalculator==0.9 -# homeassistant.components.yessssms.notify +# homeassistant.components.yessssms YesssSMS==0.2.3 # homeassistant.components.abode abodepy==0.15.0 -# homeassistant.components.frontier_silicon.media_player +# homeassistant.components.frontier_silicon afsapi==0.0.4 # homeassistant.components.ambient_station -aioambient==0.2.0 +aioambient==0.1.3 # homeassistant.components.asuswrt aioasuswrt==1.1.21 -# homeassistant.components.automatic.device_tracker +# homeassistant.components.automatic aioautomatic==0.6.5 # homeassistant.components.aws aiobotocore==0.10.2 -# homeassistant.components.dnsip.sensor +# homeassistant.components.dnsip aiodns==1.1.1 # homeassistant.components.esphome @@ -117,11 +117,11 @@ aioesphomeapi==1.7.0 # homeassistant.components.freebox aiofreepybox==0.0.8 -# homeassistant.components.yi.camera +# homeassistant.components.yi aioftp==0.12.0 -# homeassistant.components.harmony.remote -aioharmony==0.1.11 +# homeassistant.components.harmony +aioharmony==0.1.8 # homeassistant.components.emulated_hue # homeassistant.components.http @@ -130,85 +130,85 @@ aiohttp_cors==0.7.0 # homeassistant.components.hue aiohue==1.9.1 -# homeassistant.components.imap.sensor +# homeassistant.components.imap aioimaplib==0.7.15 # homeassistant.components.lifx aiolifx==0.6.7 -# homeassistant.components.lifx.light +# homeassistant.components.lifx aiolifx_effects==0.2.1 -# homeassistant.components.hunterdouglas_powerview.scene +# homeassistant.components.hunterdouglas_powerview aiopvapi==1.6.14 # homeassistant.components.unifi aiounifi==4 -# homeassistant.components.aladdin_connect.cover +# homeassistant.components.aladdin_connect aladdin_connect==0.3 # homeassistant.components.alarmdecoder alarmdecoder==1.13.2 -# homeassistant.components.alpha_vantage.sensor +# homeassistant.components.alpha_vantage alpha_vantage==2.1.0 # homeassistant.components.amcrest amcrest==1.3.0 -# homeassistant.components.androidtv.media_player +# homeassistant.components.androidtv androidtv==0.0.14 -# homeassistant.components.anel_pwrctrl.switch +# homeassistant.components.anel_pwrctrl anel_pwrctrl-homeassistant==0.0.1.dev2 -# homeassistant.components.anthemav.media_player +# homeassistant.components.anthemav anthemav==1.1.10 # homeassistant.components.apcupsd apcaccess==0.0.13 -# homeassistant.components.apns.notify +# homeassistant.components.apns apns2==0.3.0 # homeassistant.components.aqualogic aqualogic==1.0 -# homeassistant.components.ampio.air_quality +# homeassistant.components.ampio asmog==0.0.6 # homeassistant.components.asterisk_mbox asterisk_mbox==0.5.0 +# homeassistant.components.dlna_dmr # homeassistant.components.upnp -# homeassistant.components.dlna_dmr.media_player async-upnp-client==0.14.7 # homeassistant.components.stream av==6.1.2 -# homeassistant.components.avion.light +# homeassistant.components.avion # avion==0.10 # homeassistant.components.axis axis==19 -# homeassistant.components.baidu.tts +# homeassistant.components.baidu baidu-aip==1.6.6 -# homeassistant.components.modem_callerid.sensor +# homeassistant.components.modem_callerid basicmodem==0.7 -# homeassistant.components.linux_battery.sensor +# homeassistant.components.linux_battery batinfo==0.4.2 -# homeassistant.components.eddystone_temperature.sensor +# homeassistant.components.eddystone_temperature # beacontools[scan]==1.2.3 -# homeassistant.components.linksys_ap.device_tracker -# homeassistant.components.scrape.sensor -# homeassistant.components.sytadin.sensor +# homeassistant.components.linksys_ap +# homeassistant.components.scrape +# homeassistant.components.sytadin beautifulsoup4==4.7.1 # homeassistant.components.zha @@ -220,135 +220,127 @@ bimmer_connected==0.5.3 # homeassistant.components.blink blinkpy==0.13.1 -# homeassistant.components.blinksticklight.light +# homeassistant.components.blinksticklight blinkstick==1.1.8 -# homeassistant.components.blinkt.light +# homeassistant.components.blinkt # blinkt==0.1.0 -# homeassistant.components.bitcoin.sensor +# homeassistant.components.bitcoin blockchain==1.4.4 -# homeassistant.components.decora.light +# homeassistant.components.decora # bluepy==1.1.4 -# homeassistant.components.bme680.sensor +# homeassistant.components.bme680 # bme680==1.0.5 +# homeassistant.components.amazon_polly +# homeassistant.components.aws_lambda +# homeassistant.components.aws_sns +# homeassistant.components.aws_sqs # homeassistant.components.route53 -# homeassistant.components.amazon_polly.tts boto3==1.9.16 -# homeassistant.components.braviatv.media_player +# homeassistant.components.braviatv braviarc-homeassistant==0.3.7.dev0 -# homeassistant.components.broadlink.sensor -# homeassistant.components.broadlink.switch +# homeassistant.components.broadlink broadlink==0.9.0 -# homeassistant.components.brottsplatskartan.sensor +# homeassistant.components.brottsplatskartan brottsplatskartan==0.0.1 -# homeassistant.components.brunt.cover +# homeassistant.components.brunt brunt==0.1.3 -# homeassistant.components.bluetooth_tracker.device_tracker +# homeassistant.components.bluetooth_tracker bt_proximity==0.1.2 -# homeassistant.components.bt_home_hub_5.device_tracker +# homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 -# homeassistant.components.bt_smarthub.device_tracker +# homeassistant.components.bt_smarthub btsmarthub_devicelist==0.1.3 -# homeassistant.components.buienradar.sensor -# homeassistant.components.buienradar.weather +# homeassistant.components.buienradar buienradar==0.91 -# homeassistant.components.caldav.calendar +# homeassistant.components.caldav caldav==0.5.0 -# homeassistant.components.cisco_mobility_express.device_tracker +# homeassistant.components.cisco_mobility_express ciscomobilityexpress==0.1.5 -# homeassistant.components.ciscospark.notify +# homeassistant.components.ciscospark ciscosparkapi==0.4.2 -# homeassistant.components.cppm_tracker.device_tracker +# homeassistant.components.cppm_tracker clearpasspy==1.0.2 -# homeassistant.components.co2signal.sensor +# homeassistant.components.co2signal co2signal==0.4.2 # homeassistant.components.coinbase coinbase==2.1.0 -# homeassistant.components.coinmarketcap.sensor +# homeassistant.components.coinmarketcap coinmarketcap==5.0.3 # homeassistant.scripts.check_config colorlog==4.0.2 -# homeassistant.components.concord232.alarm_control_panel -# homeassistant.components.concord232.binary_sensor +# homeassistant.components.concord232 concord232==0.15 -# homeassistant.components.eddystone_temperature.sensor -# homeassistant.components.eq3btsmart.climate -# homeassistant.components.xiaomi_miio.device_tracker -# homeassistant.components.xiaomi_miio.fan -# homeassistant.components.xiaomi_miio.light -# homeassistant.components.xiaomi_miio.remote -# homeassistant.components.xiaomi_miio.sensor -# homeassistant.components.xiaomi_miio.switch -# homeassistant.components.xiaomi_miio.vacuum +# homeassistant.components.eddystone_temperature +# homeassistant.components.eq3btsmart +# homeassistant.components.xiaomi_miio construct==2.9.45 # homeassistant.scripts.credstash # credstash==1.15.0 -# homeassistant.components.crimereports.sensor +# homeassistant.components.crimereports crimereports==1.0.1 # homeassistant.components.datadog datadog==0.15.0 -# homeassistant.components.metoffice.sensor -# homeassistant.components.metoffice.weather +# homeassistant.components.metoffice datapoint==0.4.3 -# homeassistant.components.decora.light +# homeassistant.components.decora # decora==0.6 -# homeassistant.components.decora_wifi.light +# homeassistant.components.decora_wifi # decora_wifi==1.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns -# homeassistant.components.ohmconnect.sensor -# homeassistant.components.upc_connect.device_tracker +# homeassistant.components.ohmconnect +# homeassistant.components.upc_connect defusedxml==0.5.0 -# homeassistant.components.deluge.sensor -# homeassistant.components.deluge.switch +# homeassistant.components.deluge deluge-client==1.4.0 -# homeassistant.components.denonavr.media_player +# homeassistant.components.denonavr denonavr==0.7.8 -# homeassistant.components.directv.media_player +# homeassistant.components.directv directpy==0.5 -# homeassistant.components.discogs.sensor +# homeassistant.components.discogs discogs_client==2.2.1 -# homeassistant.components.discord.notify +# homeassistant.components.discord discord.py==0.16.12 # homeassistant.components.updater distro==1.4.0 -# homeassistant.components.digitalloggers.switch +# homeassistant.components.digitalloggers dlipower==0.7.165 # homeassistant.components.doorbird @@ -357,11 +349,10 @@ doorbirdpy==2.0.6 # homeassistant.components.dovado dovado==0.4.1 -# homeassistant.components.dsmr.sensor +# homeassistant.components.dsmr dsmr_parser==0.12 # homeassistant.components.dweet -# homeassistant.components.dweet.sensor dweepy==0.3.0 # homeassistant.components.ebusd @@ -373,10 +364,10 @@ ecoaliface==0.4.0 # homeassistant.components.edp_redy edp_redy==0.0.3 -# homeassistant.components.ee_brightbox.device_tracker +# homeassistant.components.ee_brightbox eebrightbox==0.0.4 -# homeassistant.components.eliqonline.sensor +# homeassistant.components.eliqonline eliqonline==1.2.2 # homeassistant.components.elkm1 @@ -388,19 +379,19 @@ emulated_roku==0.1.8 # homeassistant.components.enocean enocean==0.40 -# homeassistant.components.entur_public_transport.sensor +# homeassistant.components.entur_public_transport enturclient==0.2.0 -# homeassistant.components.envirophat.sensor +# homeassistant.components.envirophat # envirophat==0.0.6 -# homeassistant.components.enphase_envoy.sensor +# homeassistant.components.enphase_envoy envoy_reader==0.3 -# homeassistant.components.season.sensor +# homeassistant.components.season ephem==3.7.6.0 -# homeassistant.components.epson.media_player +# homeassistant.components.epson epson-projector==0.1.3 # homeassistant.components.netgear_lte @@ -410,17 +401,17 @@ eternalegypt==0.0.6 # evdev==0.6.1 # homeassistant.components.evohome -# homeassistant.components.honeywell.climate +# homeassistant.components.honeywell evohomeclient==0.3.2 -# homeassistant.components.dlib_face_detect.image_processing -# homeassistant.components.dlib_face_identify.image_processing +# homeassistant.components.dlib_face_detect +# homeassistant.components.dlib_face_identify # face_recognition==1.2.3 # homeassistant.components.fastdotcom fastdotcom==0.0.3 -# homeassistant.components.fedex.sensor +# homeassistant.components.fedex fedexdeliverymanager==1.0.6 # homeassistant.components.feedreader @@ -429,56 +420,56 @@ feedparser-homeassistant==5.2.2.dev1 # homeassistant.components.fibaro fiblary3==0.1.7 -# homeassistant.components.fints.sensor +# homeassistant.components.fints fints==1.0.1 -# homeassistant.components.fitbit.sensor +# homeassistant.components.fitbit fitbit==0.3.0 -# homeassistant.components.fixer.sensor +# homeassistant.components.fixer fixerio==1.0.0a0 -# homeassistant.components.flux_led.light +# homeassistant.components.flux_led flux_led==0.22 -# homeassistant.components.foobot.sensor +# homeassistant.components.foobot foobot_async==0.3.1 -# homeassistant.components.free_mobile.notify +# homeassistant.components.free_mobile freesms==0.1.2 -# homeassistant.components.fritz.device_tracker -# homeassistant.components.fritzbox_callmonitor.sensor -# homeassistant.components.fritzbox_netmonitor.sensor +# homeassistant.components.fritz +# homeassistant.components.fritzbox_callmonitor +# homeassistant.components.fritzbox_netmonitor # fritzconnection==0.6.5 -# homeassistant.components.fritzdect.switch +# homeassistant.components.fritzdect fritzhome==1.0.4 -# homeassistant.components.google.tts +# homeassistant.components.google gTTS-token==1.1.3 -# homeassistant.components.gearbest.sensor +# homeassistant.components.gearbest gearbest_parser==1.0.7 -# homeassistant.components.geizhals.sensor +# homeassistant.components.geizhals geizhals==0.0.9 -# homeassistant.components.geo_json_events.geo_location -# homeassistant.components.nsw_rural_fire_service_feed.geo_location -# homeassistant.components.usgs_earthquakes_feed.geo_location +# homeassistant.components.geo_json_events +# homeassistant.components.nsw_rural_fire_service_feed +# homeassistant.components.usgs_earthquakes_feed geojson_client==0.3 -# homeassistant.components.geo_rss_events.sensor +# homeassistant.components.geo_rss_events georss_generic_client==0.2 -# homeassistant.components.gitter.sensor +# homeassistant.components.gitter gitterpy==0.1.7 -# homeassistant.components.glances.sensor +# homeassistant.components.glances glances_api==0.2.0 -# homeassistant.components.gntp.notify +# homeassistant.components.gntp gntp==1.0.3 # homeassistant.components.google @@ -490,25 +481,25 @@ google-cloud-pubsub==0.39.1 # homeassistant.components.googlehome googledevices==1.0.2 -# homeassistant.components.google_travel_time.sensor +# homeassistant.components.google_travel_time googlemaps==2.5.1 -# homeassistant.components.gpsd.sensor +# homeassistant.components.gpsd gps3==0.33.3 # homeassistant.components.greeneye_monitor greeneye_monitor==1.0 -# homeassistant.components.greenwave.light +# homeassistant.components.greenwave greenwavereality==0.5.1 -# homeassistant.components.gstreamer.media_player +# homeassistant.components.gstreamer gstreamer-player==1.1.2 # homeassistant.components.ffmpeg ha-ffmpeg==2.0 -# homeassistant.components.philips_js.media_player +# homeassistant.components.philips_js ha-philipsjs==0.0.5 # homeassistant.components.habitica @@ -520,31 +511,31 @@ hangups==0.4.6 # homeassistant.components.cloud hass-nabucasa==0.11 -# homeassistant.components.mqtt.server +# homeassistant.components.mqtt hbmqtt==0.9.4 -# homeassistant.components.jewish_calendar.sensor +# homeassistant.components.jewish_calendar hdate==0.8.7 -# homeassistant.components.heatmiser.climate +# homeassistant.components.heatmiser heatmiserV3==0.9.1 -# homeassistant.components.hikvisioncam.switch +# homeassistant.components.hikvisioncam hikvision==0.4 -# homeassistant.components.hipchat.notify +# homeassistant.components.hipchat hipnotify==1.0.8 -# homeassistant.components.harman_kardon_avr.media_player +# homeassistant.components.harman_kardon_avr hkavr==0.0.5 # homeassistant.components.hlk_sw16 hlk-sw16==0.0.7 -# homeassistant.components.pi_hole.sensor +# homeassistant.components.pi_hole hole==0.3.0 -# homeassistant.components.workday.binary_sensor +# homeassistant.components.workday holidays==0.9.10 # homeassistant.components.frontend @@ -559,7 +550,7 @@ homekit[IP]==0.13.0 # homeassistant.components.homematicip_cloud homematicip==0.10.6 -# homeassistant.components.horizon.media_player +# homeassistant.components.horizon horimote==0.4.1 # homeassistant.components.google @@ -572,22 +563,21 @@ huawei-lte-api==1.1.5 # homeassistant.components.hydrawise hydrawiser==0.1.1 -# homeassistant.components.bh1750.sensor -# homeassistant.components.bme280.sensor -# homeassistant.components.htu21d.sensor +# homeassistant.components.bh1750 +# homeassistant.components.bme280 +# homeassistant.components.htu21d # i2csense==0.0.4 # homeassistant.components.watson_iot ibmiotf==0.3.4 -# homeassistant.components.iglo.light +# homeassistant.components.iglo iglo==1.2.7 # homeassistant.components.ihc ihcsdk==2.3.0 # homeassistant.components.influxdb -# homeassistant.components.influxdb.sensor influxdb==5.2.0 # homeassistant.components.insteon @@ -602,11 +592,10 @@ ipify==1.0.0 # homeassistant.components.verisure jsonpath==0.75 -# homeassistant.components.kodi.media_player -# homeassistant.components.kodi.notify +# homeassistant.components.kodi jsonrpc-async==0.6 -# homeassistant.components.kodi.media_player +# homeassistant.components.kodi jsonrpc-websocket==0.6 # homeassistant.scripts.keyring @@ -615,7 +604,7 @@ keyring==17.1.1 # homeassistant.scripts.keyring keyrings.alt==3.1.1 -# homeassistant.components.kiwi.lock +# homeassistant.components.kiwi kiwiki-client==0.1.1 # homeassistant.components.konnected @@ -627,44 +616,43 @@ lakeside==0.12 # homeassistant.components.dyson libpurecool==0.5.0 -# homeassistant.components.foscam.camera +# homeassistant.components.foscam libpyfoscam==1.0 -# homeassistant.components.mikrotik.device_tracker +# homeassistant.components.mikrotik librouteros==2.2.0 -# homeassistant.components.soundtouch.media_player +# homeassistant.components.soundtouch libsoundtouch==0.7.2 -# homeassistant.components.lifx_legacy.light +# homeassistant.components.lifx_legacy liffylights==0.9.4 -# homeassistant.components.osramlightify.light +# homeassistant.components.osramlightify lightify==1.0.7.2 # homeassistant.components.lightwave lightwave==0.15 -# homeassistant.components.limitlessled.light +# homeassistant.components.limitlessled limitlessled==1.1.3 # homeassistant.components.linode linode-api==4.1.9b1 -# homeassistant.components.liveboxplaytv.media_player +# homeassistant.components.liveboxplaytv liveboxplaytv==2.0.2 # homeassistant.components.lametric -# homeassistant.components.lametric.notify lmnotify==0.0.4 -# homeassistant.components.google_maps.device_tracker +# homeassistant.components.google_maps locationsharinglib==3.0.11 # homeassistant.components.logi_circle logi_circle==0.1.7 -# homeassistant.components.london_underground.sensor +# homeassistant.components.london_underground london-tube-status==0.2 # homeassistant.components.luftdaten @@ -673,13 +661,13 @@ luftdaten==0.3.4 # homeassistant.components.lupusec lupupy==0.0.17 -# homeassistant.components.lw12wifi.light +# homeassistant.components.lw12wifi lw12==0.9.2 -# homeassistant.components.lyft.sensor +# homeassistant.components.lyft lyft_rides==0.2 -# homeassistant.components.magicseaweed.sensor +# homeassistant.components.magicseaweed magicseaweed==1.0.3 # homeassistant.components.matrix @@ -691,23 +679,22 @@ maxcube-api==0.1.0 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 -# homeassistant.components.message_bird.notify +# homeassistant.components.message_bird messagebird==1.2.0 # homeassistant.components.meteo_france meteofrance==0.3.4 -# homeassistant.components.mfi.sensor -# homeassistant.components.mfi.switch +# homeassistant.components.mfi mficlient==0.3.0 -# homeassistant.components.miflora.sensor +# homeassistant.components.miflora miflora==0.4.0 -# homeassistant.components.mill.climate +# homeassistant.components.mill millheater==0.3.4 -# homeassistant.components.mitemp_bt.sensor +# homeassistant.components.mitemp_bt mitemp_bt==0.0.1 # homeassistant.components.mopar @@ -728,95 +715,95 @@ myusps==1.3.2 # homeassistant.components.n26 n26==0.2.7 -# homeassistant.components.nad.media_player +# homeassistant.components.nad nad_receiver==0.0.11 -# homeassistant.components.keenetic_ndms2.device_tracker +# homeassistant.components.keenetic_ndms2 ndms2_client==0.0.6 # homeassistant.components.ness_alarm nessclient==0.9.15 -# homeassistant.components.netdata.sensor +# homeassistant.components.netdata netdata==0.1.2 # homeassistant.components.discovery netdisco==2.6.0 -# homeassistant.components.neurio_energy.sensor +# homeassistant.components.neurio_energy neurio==0.3.1 -# homeassistant.components.niko_home_control.light +# homeassistant.components.niko_home_control niko-home-control==0.1.8 -# homeassistant.components.nilu.air_quality +# homeassistant.components.nilu niluclient==0.1.2 -# homeassistant.components.nederlandse_spoorwegen.sensor +# homeassistant.components.nederlandse_spoorwegen nsapi==2.7.4 -# homeassistant.components.nsw_fuel_station.sensor +# homeassistant.components.nsw_fuel_station nsw-fuel-api-client==1.0.10 # homeassistant.components.nuheat nuheat==0.3.0 -# homeassistant.components.opencv.image_processing -# homeassistant.components.pollen.sensor -# homeassistant.components.tensorflow.image_processing -# homeassistant.components.trend.binary_sensor +# homeassistant.components.opencv +# homeassistant.components.pollen +# homeassistant.components.tensorflow +# homeassistant.components.trend numpy==1.16.2 # homeassistant.components.google oauth2client==4.0.0 -# homeassistant.components.oem.climate +# homeassistant.components.oem oemthermostat==1.1 -# homeassistant.components.onkyo.media_player +# homeassistant.components.onkyo onkyo-eiscp==1.2.4 -# homeassistant.components.onvif.camera +# homeassistant.components.onvif onvif-py3==0.1.3 -# homeassistant.components.openevse.sensor +# homeassistant.components.openevse openevsewifi==0.4 -# homeassistant.components.openhome.media_player +# homeassistant.components.openhome openhomedevice==0.4.2 -# homeassistant.components.opensensemap.air_quality +# homeassistant.components.opensensemap opensensemap-api==0.1.5 -# homeassistant.components.enigma2.media_player +# homeassistant.components.enigma2 openwebifpy==3.1.0 -# homeassistant.components.luci.device_tracker +# homeassistant.components.luci openwrt-luci-rpc==1.0.5 -# homeassistant.components.orvibo.switch +# homeassistant.components.orvibo orvibo==1.1.1 # homeassistant.components.mqtt # homeassistant.components.shiftr paho-mqtt==1.4.0 -# homeassistant.components.panasonic_bluray.media_player +# homeassistant.components.panasonic_bluray panacotta==0.1 -# homeassistant.components.panasonic_viera.media_player +# homeassistant.components.panasonic_viera panasonic_viera==0.3.2 -# homeassistant.components.dunehd.media_player +# homeassistant.components.dunehd pdunehd==1.3 -# homeassistant.components.pencom.switch +# homeassistant.components.pencom pencompy==0.0.3 -# homeassistant.components.aruba.device_tracker -# homeassistant.components.cisco_ios.device_tracker -# homeassistant.components.pandora.media_player -# homeassistant.components.unifi_direct.device_tracker +# homeassistant.components.aruba +# homeassistant.components.cisco_ios +# homeassistant.components.pandora +# homeassistant.components.unifi_direct pexpect==4.6.0 # homeassistant.components.rpi_pfio @@ -825,69 +812,67 @@ pifacecommon==4.2.2 # homeassistant.components.rpi_pfio pifacedigitalio==3.0.5 -# homeassistant.components.piglow.light +# homeassistant.components.piglow piglow==1.2.4 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.proxy.camera -# homeassistant.components.qrcode.image_processing -# homeassistant.components.tensorflow.image_processing +# homeassistant.components.proxy +# homeassistant.components.qrcode +# homeassistant.components.tensorflow pillow==5.4.1 # homeassistant.components.dominos pizzapi==0.0.3 -# homeassistant.components.plex.media_player -# homeassistant.components.plex.sensor +# homeassistant.components.plex plexapi==3.0.6 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 -# homeassistant.components.mhz19.sensor -# homeassistant.components.serial_pm.sensor +# homeassistant.components.mhz19 +# homeassistant.components.serial_pm pmsensor==0.4 -# homeassistant.components.pocketcasts.sensor +# homeassistant.components.pocketcasts pocketcasts==0.1 -# homeassistant.components.postnl.sensor +# homeassistant.components.postnl postnl_api==1.0.2 -# homeassistant.components.reddit.sensor +# homeassistant.components.reddit praw==6.1.1 -# homeassistant.components.islamic_prayer_times.sensor +# homeassistant.components.islamic_prayer_times prayer_times_calculator==0.0.3 -# homeassistant.components.prezzibenzina.sensor +# homeassistant.components.prezzibenzina prezzibenzina-py==1.1.4 -# homeassistant.components.proliphix.climate +# homeassistant.components.proliphix proliphix==0.4.1 # homeassistant.components.prometheus prometheus_client==0.2.0 -# homeassistant.components.tensorflow.image_processing +# homeassistant.components.tensorflow protobuf==3.6.1 -# homeassistant.components.systemmonitor.sensor +# homeassistant.components.systemmonitor psutil==5.6.1 # homeassistant.components.wink pubnubsub-handler==1.0.3 -# homeassistant.components.pushbullet.notify -# homeassistant.components.pushbullet.sensor +# homeassistant.components.pushbullet pushbullet.py==0.11.0 -# homeassistant.components.pushetta.notify +# homeassistant.components.pushetta pushetta==1.0.15 -# homeassistant.components.rpi_gpio_pwm.light +# homeassistant.components.rpi_gpio_pwm pwmled==1.4.1 # homeassistant.components.august @@ -896,16 +881,16 @@ py-august==0.7.0 # homeassistant.components.canary py-canary==0.5.0 -# homeassistant.components.cpuspeed.sensor +# homeassistant.components.cpuspeed py-cpuinfo==5.0.0 # homeassistant.components.melissa py-melissa-climate==2.0.0 -# homeassistant.components.synology.camera +# homeassistant.components.synology py-synology==0.2.0 -# homeassistant.components.seventeentrack.sensor +# homeassistant.components.seventeentrack py17track==2.2.2 # homeassistant.components.hdmi_cec @@ -914,38 +899,38 @@ pyCEC==0.4.13 # homeassistant.components.tplink pyHS100==0.3.4 -# homeassistant.components.met.weather -# homeassistant.components.norway_air.air_quality +# homeassistant.components.met +# homeassistant.components.norway_air pyMetno==0.4.6 # homeassistant.components.rfxtrx pyRFXtrx==0.23 -# homeassistant.components.switchmate.switch +# homeassistant.components.switchmate # pySwitchmate==0.4.5 # homeassistant.components.tibber pyTibber==0.10.1 -# homeassistant.components.dlink.switch +# homeassistant.components.dlink pyW215==0.6.0 # homeassistant.components.w800rf32 pyW800rf32==0.1 -# homeassistant.components.noaa_tides.sensor +# homeassistant.components.noaa_tides # py_noaa==0.3.0 # homeassistant.components.ads pyads==3.0.7 -# homeassistant.components.aftership.sensor +# homeassistant.components.aftership pyaftership==0.1.2 -# homeassistant.components.airvisual.sensor +# homeassistant.components.airvisual pyairvisual==3.0.1 -# homeassistant.components.alarmdotcom.alarm_control_panel +# homeassistant.components.alarmdotcom pyalarmdotcom==0.3.2 # homeassistant.components.arlo @@ -957,14 +942,13 @@ pyatmo==1.9 # homeassistant.components.apple_tv pyatv==0.3.12 -# homeassistant.components.bbox.device_tracker -# homeassistant.components.bbox.sensor +# homeassistant.components.bbox pybbox==0.0.5-alpha -# homeassistant.components.blackbird.media_player +# homeassistant.components.blackbird pyblackbird==0.5 -# homeassistant.components.bluetooth_tracker.device_tracker +# homeassistant.components.bluetooth_tracker # pybluez==0.22 # homeassistant.components.neato @@ -976,25 +960,25 @@ pycarwings2==2.8 # homeassistant.components.cloudflare pycfdns==0.0.1 -# homeassistant.components.channels.media_player +# homeassistant.components.channels pychannels==1.0.0 # homeassistant.components.cast pychromecast==3.2.0 -# homeassistant.components.cmus.media_player +# homeassistant.components.cmus pycmus==0.1.1 # homeassistant.components.comfoconnect pycomfoconnect==0.3 -# homeassistant.components.coolmaster.climate +# homeassistant.components.coolmaster pycoolmasternet==0.0.4 -# homeassistant.components.microsoft.tts +# homeassistant.components.microsoft pycsspeechtts==1.0.2 -# homeassistant.components.cups.sensor +# homeassistant.components.cups # pycups==1.9.73 # homeassistant.components.daikin @@ -1012,46 +996,46 @@ pydispatcher==2.0.5 # homeassistant.components.android_ip_webcam pydroid-ipcam==0.8 -# homeassistant.components.duke_energy.sensor +# homeassistant.components.duke_energy pydukeenergy==0.0.6 -# homeassistant.components.ebox.sensor +# homeassistant.components.ebox pyebox==1.1.4 -# homeassistant.components.econet.water_heater +# homeassistant.components.econet pyeconet==0.0.10 -# homeassistant.components.edimax.switch +# homeassistant.components.edimax pyedimax==0.1 # homeassistant.components.eight_sleep pyeight==0.1.1 -# homeassistant.components.emby.media_player +# homeassistant.components.emby pyemby==1.6 # homeassistant.components.envisalink pyenvisalink==3.8 -# homeassistant.components.ephember.climate +# homeassistant.components.ephember pyephember==0.2.0 -# homeassistant.components.everlights.light +# homeassistant.components.everlights pyeverlights==0.1.0 -# homeassistant.components.fido.sensor +# homeassistant.components.fido pyfido==2.1.1 -# homeassistant.components.flexit.climate +# homeassistant.components.flexit pyflexit==0.3 -# homeassistant.components.flic.binary_sensor +# homeassistant.components.flic pyflic-homeassistant==0.4.dev0 -# homeassistant.components.flunearyou.sensor +# homeassistant.components.flunearyou pyflunearyou==1.0.3 -# homeassistant.components.futurenow.light +# homeassistant.components.futurenow pyfnip==0.2 # homeassistant.components.fritzbox @@ -1060,26 +1044,26 @@ pyfritzhome==0.4.0 # homeassistant.components.ifttt pyfttt==0.3 -# homeassistant.components.bluetooth_le_tracker.device_tracker -# homeassistant.components.skybeacon.sensor +# homeassistant.components.bluetooth_le_tracker +# homeassistant.components.skybeacon pygatt[GATTTOOL]==3.2.0 -# homeassistant.components.gogogate2.cover +# homeassistant.components.gogogate2 pygogogate2==0.1.1 -# homeassistant.components.gtfs.sensor +# homeassistant.components.gtfs pygtfs==0.1.5 -# homeassistant.components.gtt.sensor +# homeassistant.components.gtt pygtt==1.1.2 -# homeassistant.components.version.sensor +# homeassistant.components.version pyhaversion==2.0.3 # homeassistant.components.heos pyheos==0.3.0 -# homeassistant.components.hikvision.binary_sensor +# homeassistant.components.hikvision pyhik==0.2.2 # homeassistant.components.hive @@ -1091,56 +1075,55 @@ pyhomematic==0.1.58 # homeassistant.components.homeworks pyhomeworks==0.0.6 -# homeassistant.components.hydroquebec.sensor +# homeassistant.components.hydroquebec pyhydroquebec==2.2.2 -# homeassistant.components.ialarm.alarm_control_panel +# homeassistant.components.ialarm pyialarm==0.3 -# homeassistant.components.icloud.device_tracker +# homeassistant.components.icloud pyicloud==0.9.1 -# homeassistant.components.ipma.weather +# homeassistant.components.ipma pyipma==1.2.1 -# homeassistant.components.irish_rail_transport.sensor +# homeassistant.components.irish_rail_transport pyirishrail==0.0.2 -# homeassistant.components.iss.binary_sensor +# homeassistant.components.iss pyiss==1.0.1 -# homeassistant.components.itach.remote +# homeassistant.components.itach pyitachip2ir==0.0.7 # homeassistant.components.kira pykira==0.1.1 -# homeassistant.components.kwb.sensor +# homeassistant.components.kwb pykwb==0.0.8 -# homeassistant.components.lacrosse.sensor +# homeassistant.components.lacrosse pylacrosse==0.3.1 -# homeassistant.components.lastfm.sensor +# homeassistant.components.lastfm pylast==3.1.0 -# homeassistant.components.launch_library.sensor +# homeassistant.components.launch_library pylaunches==0.2.0 -# homeassistant.components.lg_netcast.media_player +# homeassistant.components.lg_netcast pylgnetcast-homeassistant==0.2.0.dev0 -# homeassistant.components.webostv.media_player -# homeassistant.components.webostv.notify +# homeassistant.components.webostv pylgtv==0.1.9 -# homeassistant.components.linky.sensor +# homeassistant.components.linky pylinky==0.3.3 # homeassistant.components.litejet pylitejet==0.1 -# homeassistant.components.loopenergy.sensor +# homeassistant.components.loopenergy pyloopenergy==0.1.2 # homeassistant.components.lutron_caseta @@ -1149,13 +1132,13 @@ pylutron-caseta==0.5.0 # homeassistant.components.lutron pylutron==0.2.0 -# homeassistant.components.mailgun.notify +# homeassistant.components.mailgun pymailgunner==1.4 -# homeassistant.components.mediaroom.media_player +# homeassistant.components.mediaroom pymediaroom==0.6.4 -# homeassistant.components.xiaomi_tv.media_player +# homeassistant.components.xiaomi_tv pymitv==1.4.3 # homeassistant.components.mochad @@ -1164,44 +1147,43 @@ pymochad==0.2.0 # homeassistant.components.modbus pymodbus==1.5.2 -# homeassistant.components.monoprice.media_player +# homeassistant.components.monoprice pymonoprice==0.3 -# homeassistant.components.yamaha_musiccast.media_player +# homeassistant.components.yamaha_musiccast pymusiccast==0.1.6 -# homeassistant.components.myq.cover +# homeassistant.components.myq pymyq==1.1.0 # homeassistant.components.mysensors pymysensors==0.18.0 -# homeassistant.components.nanoleaf.light +# homeassistant.components.nanoleaf pynanoleaf==0.0.5 -# homeassistant.components.nello.lock +# homeassistant.components.nello pynello==2.0.2 -# homeassistant.components.netgear.device_tracker +# homeassistant.components.netgear pynetgear==0.5.2 -# homeassistant.components.netio.switch +# homeassistant.components.netio pynetio==0.1.9.1 -# homeassistant.components.nuki.lock +# homeassistant.components.nuki pynuki==1.3.2 -# homeassistant.components.nut.sensor +# homeassistant.components.nut pynut2==2.1.2 -# homeassistant.components.nx584.alarm_control_panel -# homeassistant.components.nx584.binary_sensor +# homeassistant.components.nx584 pynx584==0.4 # homeassistant.components.openuv pyopenuv==1.0.9 -# homeassistant.components.opple.light +# homeassistant.components.opple pyoppleio==1.0.5 # homeassistant.components.iota @@ -1212,26 +1194,25 @@ pyotgw==0.4b3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp -# homeassistant.components.otp.sensor +# homeassistant.components.otp pyotp==2.2.6 # homeassistant.components.owlet pyowlet==1.0.2 -# homeassistant.components.openweathermap.sensor -# homeassistant.components.openweathermap.weather +# homeassistant.components.openweathermap pyowm==2.10.0 # homeassistant.components.lcn pypck==0.5.9 -# homeassistant.components.pjlink.media_player +# homeassistant.components.pjlink pypjlink2==1.2.0 # homeassistant.components.point pypoint==1.1.1 -# homeassistant.components.pollen.sensor +# homeassistant.components.pollen pypollencom==2.2.3 # homeassistant.components.ps4 @@ -1240,40 +1221,40 @@ pyps4-homeassistant==0.5.2 # homeassistant.components.qwikswitch pyqwikswitch==0.93 -# homeassistant.components.nmbs.sensor +# homeassistant.components.nmbs pyrail==0.0.3 # homeassistant.components.rainbird pyrainbird==0.1.6 -# homeassistant.components.recswitch.switch +# homeassistant.components.recswitch pyrecswitch==1.0.2 -# homeassistant.components.ruter.sensor +# homeassistant.components.ruter pyruter==1.1.0 # homeassistant.components.sabnzbd pysabnzbd==1.1.0 -# homeassistant.components.sony_projector.switch +# homeassistant.components.sony_projector pysdcp==1 -# homeassistant.components.sensibo.climate +# homeassistant.components.sensibo pysensibo==1.0.3 -# homeassistant.components.serial.sensor +# homeassistant.components.serial pyserial-asyncio==0.4 -# homeassistant.components.acer_projector.switch +# homeassistant.components.acer_projector pyserial==3.1.1 -# homeassistant.components.sesame.lock +# homeassistant.components.sesame pysesame==0.1.0 # homeassistant.components.goalfeed pysher==1.0.1 -# homeassistant.components.sma.sensor +# homeassistant.components.sma pysma==0.3.1 # homeassistant.components.smartthings @@ -1282,9 +1263,7 @@ pysmartapp==0.3.2 # homeassistant.components.smartthings pysmartthings==0.6.7 -# homeassistant.components.snmp.device_tracker -# homeassistant.components.snmp.sensor -# homeassistant.components.snmp.switch +# homeassistant.components.snmp pysnmp==4.4.8 # homeassistant.components.sonos @@ -1293,29 +1272,28 @@ pysonos==0.0.8 # homeassistant.components.spc pyspcwebgw==0.4.0 -# homeassistant.components.stride.notify +# homeassistant.components.stride pystride==0.1.7 -# homeassistant.components.syncthru.sensor +# homeassistant.components.syncthru pysyncthru==0.3.1 -# homeassistant.components.tautulli.sensor +# homeassistant.components.tautulli pytautulli==0.5.0 -# homeassistant.components.liveboxplaytv.media_player +# homeassistant.components.liveboxplaytv pyteleloisirs==3.4 -# homeassistant.components.tfiac.climate +# homeassistant.components.tfiac pytfiac==0.3 -# homeassistant.components.thinkingcleaner.sensor -# homeassistant.components.thinkingcleaner.switch +# homeassistant.components.thinkingcleaner pythinkingcleaner==0.0.3 -# homeassistant.components.blockchain.sensor +# homeassistant.components.blockchain python-blockchain-api==0.0.2 -# homeassistant.components.clementine.media_player +# homeassistant.components.clementine python-clementine-remote==1.0.1 # homeassistant.components.digital_ocean @@ -1324,30 +1302,28 @@ python-digitalocean==1.13.2 # homeassistant.components.ecobee python-ecobee-api==0.0.18 -# homeassistant.components.eq3btsmart.climate +# homeassistant.components.eq3btsmart # python-eq3bt==0.1.9 -# homeassistant.components.etherscan.sensor +# homeassistant.components.etherscan python-etherscan-api==0.0.3 -# homeassistant.components.familyhub.camera +# homeassistant.components.familyhub python-family-hub-local==0.0.2 -# homeassistant.components.darksky.sensor -# homeassistant.components.darksky.weather +# homeassistant.components.darksky python-forecastio==1.4.0 # homeassistant.components.gc100 python-gc100==1.0.3a -# homeassistant.components.gitlab_ci.sensor +# homeassistant.components.gitlab_ci python-gitlab==1.6.0 -# homeassistant.components.hp_ilo.sensor +# homeassistant.components.hp_ilo python-hpilo==3.9 # homeassistant.components.joaoapps_join -# homeassistant.components.joaoapps_join.notify python-join-api==0.0.4 # homeassistant.components.juicenet @@ -1356,47 +1332,40 @@ python-juicenet==0.0.5 # homeassistant.components.lirc # python-lirc==1.2.3 -# homeassistant.components.xiaomi_miio.device_tracker -# homeassistant.components.xiaomi_miio.fan -# homeassistant.components.xiaomi_miio.light -# homeassistant.components.xiaomi_miio.remote -# homeassistant.components.xiaomi_miio.sensor -# homeassistant.components.xiaomi_miio.switch -# homeassistant.components.xiaomi_miio.vacuum +# homeassistant.components.xiaomi_miio python-miio==0.4.5 -# homeassistant.components.mpd.media_player +# homeassistant.components.mpd python-mpd2==1.0.0 -# homeassistant.components.mystrom.light -# homeassistant.components.mystrom.switch +# homeassistant.components.mystrom python-mystrom==0.5.0 # homeassistant.components.nest python-nest==4.1.0 -# homeassistant.components.nmap_tracker.device_tracker +# homeassistant.components.nmap_tracker python-nmap==0.6.1 -# homeassistant.components.pushover.notify +# homeassistant.components.pushover python-pushover==0.3 -# homeassistant.components.qbittorrent.sensor +# homeassistant.components.qbittorrent python-qbittorrent==0.3.1 -# homeassistant.components.ripple.sensor +# homeassistant.components.ripple python-ripple-api==0.0.3 # homeassistant.components.roku python-roku==3.1.5 -# homeassistant.components.sochain.sensor +# homeassistant.components.sochain python-sochain-api==0.0.2 -# homeassistant.components.songpal.media_player +# homeassistant.components.songpal python-songpal==0.0.9.1 -# homeassistant.components.synologydsm.sensor +# homeassistant.components.synologydsm python-synology==0.2.0 # homeassistant.components.tado @@ -1405,55 +1374,55 @@ python-tado==0.2.9 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 -# homeassistant.components.twitch.sensor +# homeassistant.components.twitch python-twitch-client==0.6.0 # homeassistant.components.velbus python-velbus==2.0.22 -# homeassistant.components.vlc.media_player +# homeassistant.components.vlc python-vlc==1.1.2 -# homeassistant.components.whois.sensor +# homeassistant.components.whois python-whois==0.7.1 # homeassistant.components.wink python-wink==1.10.3 -# homeassistant.components.awair.sensor +# homeassistant.components.awair python_awair==0.0.3 -# homeassistant.components.swiss_public_transport.sensor +# homeassistant.components.swiss_public_transport python_opendata_transport==0.1.4 # homeassistant.components.egardia pythonegardia==1.0.39 -# homeassistant.components.tile.device_tracker +# homeassistant.components.tile pytile==2.0.6 -# homeassistant.components.touchline.climate +# homeassistant.components.touchline pytouchline==0.7 -# homeassistant.components.traccar.device_tracker +# homeassistant.components.traccar pytraccar==0.5.0 -# homeassistant.components.trackr.device_tracker +# homeassistant.components.trackr pytrackr==0.0.5 # homeassistant.components.tradfri pytradfri[async]==6.0.1 -# homeassistant.components.trafikverket_weatherstation.sensor +# homeassistant.components.trafikverket_weatherstation pytrafikverket==0.1.5.9 -# homeassistant.components.ubee.device_tracker +# homeassistant.components.ubee pyubee==0.2 -# homeassistant.components.unifi.device_tracker +# homeassistant.components.unifi pyunifi==2.16 -# homeassistant.components.uptimerobot.binary_sensor +# homeassistant.components.uptimerobot pyuptimerobot==0.0.5 # homeassistant.components.keyboard @@ -1462,40 +1431,40 @@ pyuptimerobot==0.0.5 # homeassistant.components.vera pyvera==0.2.45 -# homeassistant.components.vesync.switch +# homeassistant.components.vesync pyvesync_v2==0.9.6 -# homeassistant.components.vizio.media_player +# homeassistant.components.vizio pyvizio==0.0.4 # homeassistant.components.velux pyvlx==0.2.10 -# homeassistant.components.html5.notify -pywebpush==1.9.2 +# homeassistant.components.html5 +pywebpush==1.6.0 # homeassistant.components.wemo pywemo==0.4.34 -# homeassistant.components.xeoma.camera +# homeassistant.components.xeoma pyxeoma==1.4.1 # homeassistant.components.zabbix pyzabbix==0.7.4 -# homeassistant.components.qrcode.image_processing +# homeassistant.components.qrcode pyzbar==0.1.7 -# homeassistant.components.qnap.sensor +# homeassistant.components.qnap qnapstats==0.2.7 -# homeassistant.components.quantum_gateway.device_tracker +# homeassistant.components.quantum_gateway quantum-gateway==0.0.5 # homeassistant.components.rachio rachiopy==0.1.3 -# homeassistant.components.radiotherm.climate +# homeassistant.components.radiotherm radiotherm==2.0.0 # homeassistant.components.raincloud @@ -1504,10 +1473,10 @@ raincloudy==0.0.5 # homeassistant.components.raspihats # raspihats==2.2.3 -# homeassistant.components.raspyrfm.switch +# homeassistant.components.raspyrfm raspyrfm-client==1.2.8 -# homeassistant.components.recollect_waste.sensor +# homeassistant.components.recollect_waste recollect-waste==1.0.1 # homeassistant.components.rainmachine @@ -1525,62 +1494,61 @@ rflink==0.0.37 # homeassistant.components.ring ring_doorbell==0.2.3 -# homeassistant.components.ritassist.device_tracker +# homeassistant.components.ritassist ritassist==0.9.2 -# homeassistant.components.rejseplanen.sensor +# homeassistant.components.rejseplanen rjpl==0.3.5 -# homeassistant.components.rocketchat.notify +# homeassistant.components.rocketchat rocketchat-API==0.6.1 -# homeassistant.components.roomba.vacuum +# homeassistant.components.roomba roombapy==1.3.1 -# homeassistant.components.rova.sensor +# homeassistant.components.rova rova==0.1.0 -# homeassistant.components.rpi_rf.switch +# homeassistant.components.rpi_rf # rpi-rf==0.9.7 -# homeassistant.components.russound_rnet.media_player +# homeassistant.components.russound_rnet russound==0.1.9 -# homeassistant.components.russound_rio.media_player +# homeassistant.components.russound_rio russound_rio==0.1.4 -# homeassistant.components.yamaha.media_player +# homeassistant.components.yamaha rxv==0.6.0 -# homeassistant.components.samsungtv.media_player +# homeassistant.components.samsungtv samsungctl[websocket]==0.7.1 # homeassistant.components.satel_integra satel_integra==0.3.2 -# homeassistant.components.deutsche_bahn.sensor +# homeassistant.components.deutsche_bahn schiene==0.23 # homeassistant.components.scsgate scsgate==0.1.0 -# homeassistant.components.sendgrid.notify +# homeassistant.components.sendgrid sendgrid==5.6.0 -# homeassistant.components.sensehat.light -# homeassistant.components.sensehat.sensor +# homeassistant.components.sensehat sense-hat==2.2.0 # homeassistant.components.sense sense_energy==0.7.0 -# homeassistant.components.aquostv.media_player +# homeassistant.components.aquostv sharp_aquos_rc==0.3.2 -# homeassistant.components.shodan.sensor +# homeassistant.components.shodan shodan==1.11.1 -# homeassistant.components.simplepush.notify +# homeassistant.components.simplepush simplepush==1.1.4 # homeassistant.components.simplisafe @@ -1592,39 +1560,39 @@ sisyphus-control==2.1 # homeassistant.components.skybell skybellpy==0.3.0 -# homeassistant.components.slack.notify +# homeassistant.components.slack slacker==0.12.0 # homeassistant.components.sleepiq sleepyq==0.6 -# homeassistant.components.xmpp.notify +# homeassistant.components.xmpp slixmpp==1.4.2 # homeassistant.components.smappee smappy==0.2.16 +# homeassistant.components.bh1750 +# homeassistant.components.bme280 +# homeassistant.components.bme680 +# homeassistant.components.envirophat +# homeassistant.components.htu21d # homeassistant.components.raspihats -# homeassistant.components.bh1750.sensor -# homeassistant.components.bme280.sensor -# homeassistant.components.bme680.sensor -# homeassistant.components.envirophat.sensor -# homeassistant.components.htu21d.sensor # smbus-cffi==0.5.1 # homeassistant.components.smhi smhi-pkg==1.0.10 -# homeassistant.components.snapcast.media_player +# homeassistant.components.snapcast snapcast==2.0.9 -# homeassistant.components.socialblade.sensor +# homeassistant.components.socialblade socialbladeclient==0.2 -# homeassistant.components.solaredge.sensor +# homeassistant.components.solaredge solaredge==0.0.2 -# homeassistant.components.honeywell.climate +# homeassistant.components.honeywell somecomfort==0.5.2 # homeassistant.components.speedtestdotnet @@ -1633,55 +1601,55 @@ speedtest-cli==2.1.1 # homeassistant.components.spider spiderpy==1.3.1 -# homeassistant.components.spotcrime.sensor +# homeassistant.components.spotcrime spotcrime==1.0.3 -# homeassistant.components.spotify.media_player +# homeassistant.components.spotify spotipy-homeassistant==2.4.4.dev1 # homeassistant.components.recorder -# homeassistant.components.sql.sensor +# homeassistant.components.sql sqlalchemy==1.3.0 -# homeassistant.components.srp_energy.sensor +# homeassistant.components.srp_energy srpenergy==1.0.6 -# homeassistant.components.starlingbank.sensor +# homeassistant.components.starlingbank starlingbank==3.1 # homeassistant.components.statsd statsd==3.2.1 -# homeassistant.components.steam_online.sensor +# homeassistant.components.steam_online steamodd==4.21 -# homeassistant.components.solaredge.sensor -# homeassistant.components.thermoworks_smoke.sensor -# homeassistant.components.traccar.device_tracker +# homeassistant.components.solaredge +# homeassistant.components.thermoworks_smoke +# homeassistant.components.traccar stringcase==1.2.0 # homeassistant.components.ecovacs sucks==0.9.3 -# homeassistant.components.onvif.camera +# homeassistant.components.onvif suds-passworddigest-homeassistant==0.1.2a0.dev0 -# homeassistant.components.onvif.camera +# homeassistant.components.onvif suds-py3==1.3.3.0 -# homeassistant.components.swiss_hydrological_data.sensor +# homeassistant.components.swiss_hydrological_data swisshydrodata==0.0.3 -# homeassistant.components.synology_srm.device_tracker +# homeassistant.components.synology_srm synology-srm==0.0.6 # homeassistant.components.tahoma tahoma-api==0.0.14 -# homeassistant.components.tank_utility.sensor +# homeassistant.components.tank_utility tank_utility==1.4.0 -# homeassistant.components.tapsaff.binary_sensor +# homeassistant.components.tapsaff tapsaff==0.2.0 # homeassistant.components.tellstick @@ -1693,37 +1661,37 @@ tellcore-py==1.1.2 # homeassistant.components.tellduslive tellduslive==0.10.10 -# homeassistant.components.lg_soundbar.media_player +# homeassistant.components.lg_soundbar temescal==0.1 -# homeassistant.components.temper.sensor +# homeassistant.components.temper temperusb==1.5.3 # homeassistant.components.tesla teslajsonpy==0.0.25 -# homeassistant.components.thermoworks_smoke.sensor +# homeassistant.components.thermoworks_smoke thermoworks_smoke==0.1.8 # homeassistant.components.thingspeak thingspeak==0.4.1 -# homeassistant.components.tikteck.light +# homeassistant.components.tikteck tikteck==0.4 -# homeassistant.components.todoist.calendar +# homeassistant.components.todoist todoist-python==7.0.17 # homeassistant.components.toon toonapilib==3.2.2 -# homeassistant.components.totalconnect.alarm_control_panel +# homeassistant.components.totalconnect total_connect_client==0.25 # homeassistant.components.tplink_lte tp-connected==0.0.4 -# homeassistant.components.tplink.device_tracker +# homeassistant.components.tplink tplink==0.2.1 # homeassistant.components.transmission @@ -1735,25 +1703,25 @@ tuyapy==0.1.3 # homeassistant.components.twilio twilio==6.19.1 -# homeassistant.components.uber.sensor +# homeassistant.components.uber uber_rides==0.6.0 # homeassistant.components.upcloud upcloud-api==0.4.3 -# homeassistant.components.ups.sensor +# homeassistant.components.ups upsmychoice==1.0.6 -# homeassistant.components.uscis.sensor +# homeassistant.components.uscis uscisstatus==0.1.1 -# homeassistant.components.uvc.camera +# homeassistant.components.uvc uvcclient==0.11.0 -# homeassistant.components.venstar.climate +# homeassistant.components.venstar venstarcolortouch==0.6 -# homeassistant.components.volkszaehler.sensor +# homeassistant.components.volkszaehler volkszaehler==0.1.2 # homeassistant.components.volvooncall @@ -1762,19 +1730,18 @@ volvooncall==0.8.7 # homeassistant.components.verisure vsure==1.5.2 -# homeassistant.components.vasttrafik.sensor +# homeassistant.components.vasttrafik vtjp==0.1.14 # homeassistant.components.vultr vultr==0.1.2 +# homeassistant.components.panasonic_viera +# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan -# homeassistant.components.panasonic_viera.media_player -# homeassistant.components.samsungtv.media_player -# homeassistant.components.wake_on_lan.switch wakeonlan==1.1.6 -# homeassistant.components.waqi.sensor +# homeassistant.components.waqi waqiasync==1.0.0 # homeassistant.components.folder_watcher @@ -1783,13 +1750,13 @@ watchdog==0.8.3 # homeassistant.components.waterfurnace waterfurnace==1.1.0 -# homeassistant.components.cisco_webex_teams.notify +# homeassistant.components.cisco_webex_teams webexteamssdk==1.1.1 -# homeassistant.components.gpmdp.media_player +# homeassistant.components.gpmdp websocket-client==0.54.0 -# homeassistant.components.webostv.media_player +# homeassistant.components.webostv websockets==6.0 # homeassistant.components.wirelesstag @@ -1801,42 +1768,41 @@ wunderpy2==0.1.6 # homeassistant.components.zigbee xbee-helper==0.0.7 -# homeassistant.components.xbox_live.sensor +# homeassistant.components.xbox_live xboxapi==0.1.1 -# homeassistant.components.xfinity.device_tracker +# homeassistant.components.xfinity xfinity-gateway==0.0.4 # homeassistant.components.knx xknx==0.10.0 -# homeassistant.components.bluesound.media_player -# homeassistant.components.startca.sensor -# homeassistant.components.ted5000.sensor -# homeassistant.components.yr.sensor -# homeassistant.components.zestimate.sensor +# homeassistant.components.bluesound +# homeassistant.components.startca +# homeassistant.components.ted5000 +# homeassistant.components.yr +# homeassistant.components.zestimate xmltodict==0.11.0 # homeassistant.components.xs1 xs1-api-client==2.3.5 -# homeassistant.components.yweather.sensor -# homeassistant.components.yweather.weather +# homeassistant.components.yweather yahooweather==0.10 -# homeassistant.components.yale_smart_alarm.alarm_control_panel +# homeassistant.components.yale_smart_alarm yalesmartalarmclient==0.1.6 # homeassistant.components.yeelight yeelight==0.4.4 -# homeassistant.components.yeelightsunflower.light +# homeassistant.components.yeelightsunflower yeelightsunflower==0.0.10 # homeassistant.components.media_extractor youtube_dl==2019.03.18 -# homeassistant.components.zengge.light +# homeassistant.components.zengge zengge==0.2 # homeassistant.components.zeroconf @@ -1845,10 +1811,10 @@ zeroconf==0.21.3 # homeassistant.components.zha zha-quirks==0.0.7 -# homeassistant.components.zhong_hong.climate +# homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 -# homeassistant.components.ziggo_mediabox_xl.media_player +# homeassistant.components.ziggo_mediabox_xl ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 73fbae3aadb6c7..8b26b7f4b93735 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -26,19 +26,19 @@ HAP-python==2.4.2 # homeassistant.components.owntracks PyNaCl==1.3.0 -# homeassistant.components.rmvtransport.sensor +# homeassistant.components.rmvtransport PyRMVtransport==0.1.3 -# homeassistant.components.transport_nsw.sensor +# homeassistant.components.transport_nsw PyTransportNSW==0.1.1 -# homeassistant.components.yessssms.notify +# homeassistant.components.yessssms YesssSMS==0.2.3 # homeassistant.components.ambient_station -aioambient==0.2.0 +aioambient==0.1.3 -# homeassistant.components.automatic.device_tracker +# homeassistant.components.automatic aioautomatic==0.6.5 # homeassistant.components.aws @@ -54,7 +54,7 @@ aiohue==1.9.1 # homeassistant.components.unifi aiounifi==4 -# homeassistant.components.apns.notify +# homeassistant.components.apns apns2==0.3.0 # homeassistant.components.stream @@ -66,49 +66,49 @@ axis==19 # homeassistant.components.zha bellows-homeassistant==0.7.2 -# homeassistant.components.caldav.calendar +# homeassistant.components.caldav caldav==0.5.0 -# homeassistant.components.coinmarketcap.sensor +# homeassistant.components.coinmarketcap coinmarketcap==5.0.3 # homeassistant.components.ihc # homeassistant.components.namecheapdns -# homeassistant.components.ohmconnect.sensor -# homeassistant.components.upc_connect.device_tracker +# homeassistant.components.ohmconnect +# homeassistant.components.upc_connect defusedxml==0.5.0 -# homeassistant.components.dsmr.sensor +# homeassistant.components.dsmr dsmr_parser==0.12 -# homeassistant.components.ee_brightbox.device_tracker +# homeassistant.components.ee_brightbox eebrightbox==0.0.4 # homeassistant.components.emulated_roku emulated_roku==0.1.8 -# homeassistant.components.season.sensor +# homeassistant.components.season ephem==3.7.6.0 # homeassistant.components.evohome -# homeassistant.components.honeywell.climate +# homeassistant.components.honeywell evohomeclient==0.3.2 # homeassistant.components.feedreader feedparser-homeassistant==5.2.2.dev1 -# homeassistant.components.foobot.sensor +# homeassistant.components.foobot foobot_async==0.3.1 -# homeassistant.components.google.tts +# homeassistant.components.google gTTS-token==1.1.3 -# homeassistant.components.geo_json_events.geo_location -# homeassistant.components.nsw_rural_fire_service_feed.geo_location -# homeassistant.components.usgs_earthquakes_feed.geo_location +# homeassistant.components.geo_json_events +# homeassistant.components.nsw_rural_fire_service_feed +# homeassistant.components.usgs_earthquakes_feed geojson_client==0.3 -# homeassistant.components.geo_rss_events.sensor +# homeassistant.components.geo_rss_events georss_generic_client==0.2 # homeassistant.components.ffmpeg @@ -120,13 +120,13 @@ hangups==0.4.6 # homeassistant.components.cloud hass-nabucasa==0.11 -# homeassistant.components.mqtt.server +# homeassistant.components.mqtt hbmqtt==0.9.4 -# homeassistant.components.jewish_calendar.sensor +# homeassistant.components.jewish_calendar hdate==0.8.7 -# homeassistant.components.workday.binary_sensor +# homeassistant.components.workday holidays==0.9.10 # homeassistant.components.frontend @@ -139,7 +139,6 @@ homekit[IP]==0.13.0 homematicip==0.10.6 # homeassistant.components.influxdb -# homeassistant.components.influxdb.sensor influxdb==5.2.0 # homeassistant.components.verisure @@ -148,7 +147,7 @@ jsonpath==0.75 # homeassistant.components.dyson libpurecool==0.5.0 -# homeassistant.components.soundtouch.media_player +# homeassistant.components.soundtouch libsoundtouch==0.7.2 # homeassistant.components.luftdaten @@ -157,38 +156,36 @@ luftdaten==0.3.4 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 -# homeassistant.components.mfi.sensor -# homeassistant.components.mfi.switch +# homeassistant.components.mfi mficlient==0.3.0 -# homeassistant.components.opencv.image_processing -# homeassistant.components.pollen.sensor -# homeassistant.components.tensorflow.image_processing -# homeassistant.components.trend.binary_sensor +# homeassistant.components.opencv +# homeassistant.components.pollen +# homeassistant.components.tensorflow +# homeassistant.components.trend numpy==1.16.2 # homeassistant.components.mqtt # homeassistant.components.shiftr paho-mqtt==1.4.0 -# homeassistant.components.aruba.device_tracker -# homeassistant.components.cisco_ios.device_tracker -# homeassistant.components.pandora.media_player -# homeassistant.components.unifi_direct.device_tracker +# homeassistant.components.aruba +# homeassistant.components.cisco_ios +# homeassistant.components.pandora +# homeassistant.components.unifi_direct pexpect==4.6.0 # homeassistant.components.pilight pilight==0.1.1 -# homeassistant.components.mhz19.sensor -# homeassistant.components.serial_pm.sensor +# homeassistant.components.mhz19 +# homeassistant.components.serial_pm pmsensor==0.4 # homeassistant.components.prometheus prometheus_client==0.2.0 -# homeassistant.components.pushbullet.notify -# homeassistant.components.pushbullet.sensor +# homeassistant.components.pushbullet pushbullet.py==0.11.0 # homeassistant.components.canary @@ -197,7 +194,7 @@ py-canary==0.5.0 # homeassistant.components.tplink pyHS100==0.3.4 -# homeassistant.components.blackbird.media_player +# homeassistant.components.blackbird pyblackbird==0.5 # homeassistant.components.deconz @@ -215,11 +212,10 @@ pyhomematic==0.1.58 # homeassistant.components.litejet pylitejet==0.1 -# homeassistant.components.monoprice.media_player +# homeassistant.components.monoprice pymonoprice==0.3 -# homeassistant.components.nx584.alarm_control_panel -# homeassistant.components.nx584.binary_sensor +# homeassistant.components.nx584 pynx584==0.4 # homeassistant.components.openuv @@ -227,7 +223,7 @@ pyopenuv==1.0.9 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp -# homeassistant.components.otp.sensor +# homeassistant.components.otp pyotp==2.2.6 # homeassistant.components.ps4 @@ -248,24 +244,23 @@ pysonos==0.0.8 # homeassistant.components.spc pyspcwebgw==0.4.0 -# homeassistant.components.darksky.sensor -# homeassistant.components.darksky.weather +# homeassistant.components.darksky python-forecastio==1.4.0 # homeassistant.components.nest python-nest==4.1.0 -# homeassistant.components.awair.sensor +# homeassistant.components.awair python_awair==0.0.3 # homeassistant.components.tradfri pytradfri[async]==6.0.1 -# homeassistant.components.unifi.device_tracker +# homeassistant.components.unifi pyunifi==2.16 -# homeassistant.components.html5.notify -pywebpush==1.9.2 +# homeassistant.components.html5 +pywebpush==1.6.0 # homeassistant.components.rainmachine regenmaschine==1.4.0 @@ -279,7 +274,7 @@ rflink==0.0.37 # homeassistant.components.ring ring_doorbell==0.2.3 -# homeassistant.components.yamaha.media_player +# homeassistant.components.yamaha rxv==0.6.0 # homeassistant.components.simplisafe @@ -291,14 +286,14 @@ sleepyq==0.6 # homeassistant.components.smhi smhi-pkg==1.0.10 -# homeassistant.components.honeywell.climate +# homeassistant.components.honeywell somecomfort==0.5.2 # homeassistant.components.recorder -# homeassistant.components.sql.sensor +# homeassistant.components.sql sqlalchemy==1.3.0 -# homeassistant.components.srp_energy.sensor +# homeassistant.components.srp_energy srpenergy==1.0.6 # homeassistant.components.statsd @@ -307,7 +302,7 @@ statsd==3.2.1 # homeassistant.components.toon toonapilib==3.2.2 -# homeassistant.components.uvc.camera +# homeassistant.components.uvc uvcclient==0.11.0 # homeassistant.components.verisure @@ -316,10 +311,9 @@ vsure==1.5.2 # homeassistant.components.vultr vultr==0.1.2 +# homeassistant.components.panasonic_viera +# homeassistant.components.samsungtv # homeassistant.components.wake_on_lan -# homeassistant.components.panasonic_viera.media_player -# homeassistant.components.samsungtv.media_player -# homeassistant.components.wake_on_lan.switch wakeonlan==1.1.6 # homeassistant.components.zha diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 33a7b4fd16fee1..8f6172c2323a64 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -7,6 +7,8 @@ import re import sys +from script.manifest.requirements import gather_requirements_from_manifests + COMMENT_REQUIREMENTS = ( 'Adafruit-DHT', 'Adafruit_BBIO', @@ -213,8 +215,25 @@ def gather_modules(): errors = [] + gather_requirements_from_manifests(process_requirements, errors, reqs) + gather_requirements_from_modules(errors, reqs) + + for key in reqs: + reqs[key] = sorted(reqs[key], + key=lambda name: (len(name.split('.')), name)) + + if errors: + print("******* ERROR") + print("Errors while importing: ", ', '.join(errors)) + print("Make sure you import 3rd party libraries inside methods.") + return None + + return reqs + + +def gather_requirements_from_modules(errors, reqs): + """Collect the requirements from the modules directly.""" for package in sorted( - explore_module('homeassistant.components', True) + explore_module('homeassistant.scripts', True) + explore_module('homeassistant.auth', True)): try: @@ -228,41 +247,31 @@ def gather_modules(): errors.append(package) continue - if not getattr(module, 'REQUIREMENTS', None): - continue - - for req in module.REQUIREMENTS: - if req in IGNORE_REQ: - continue - if '://' in req and 'pyharmony' not in req: - errors.append( - "{}[Only pypi dependencies are allowed: {}]".format( - package, req)) - if req.partition('==')[1] == '' and req not in IGNORE_PIN: - errors.append( - "{}[Please pin requirement {}, see {}]".format( - package, req, URL_PIN)) - reqs.setdefault(req, []).append(package) + if getattr(module, 'REQUIREMENTS', None): + process_requirements(errors, module.REQUIREMENTS, package, reqs) - for key in reqs: - reqs[key] = sorted(reqs[key], - key=lambda name: (len(name.split('.')), name)) - if errors: - print("******* ERROR") - print("Errors while importing: ", ', '.join(errors)) - print("Make sure you import 3rd party libraries inside methods.") - return None - - return reqs +def process_requirements(errors, module_requirements, package, reqs): + """Process all of the requirements.""" + for req in module_requirements: + if req in IGNORE_REQ: + continue + if '://' in req: + errors.append( + "{}[Only pypi dependencies are allowed: {}]".format( + package, req)) + if req.partition('==')[1] == '' and req not in IGNORE_PIN: + errors.append( + "{}[Please pin requirement {}, see {}]".format( + package, req, URL_PIN)) + reqs.setdefault(req, []).append(package) def generate_requirements_list(reqs): """Generate a pip file based on requirements.""" output = [] for pkg, requirements in sorted(reqs.items(), key=lambda item: item[0]): - for req in sorted(requirements, - key=lambda name: (len(name.split('.')), name)): + for req in sorted(requirements): output.append('\n# {}'.format(req)) if comment_requirement(pkg): diff --git a/script/manifest/requirements.py b/script/manifest/requirements.py new file mode 100644 index 00000000000000..5a370510484aba --- /dev/null +++ b/script/manifest/requirements.py @@ -0,0 +1,22 @@ +"""Helpers to gather requirements from manifests.""" +from .manifest_helper import iter_manifests + + +def gather_requirements_from_manifests(process_requirements, errors, reqs): + """Gather all of the requirements from manifests.""" + for manifest in iter_manifests(): + assert manifest['domain'] + + if manifest.get('requirements') is None: + errors.append( + 'The manifest for component {} is invalid. Please run' + 'script/manifest/validate.py'.format(manifest['domain']) + ) + continue + + process_requirements( + errors, + manifest['requirements'], + 'homeassistant.components.{}'.format(manifest['domain']), + reqs + ) From 8b77298908cd67d9814894abb0de7939731d9cea Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 4 Apr 2019 21:57:34 -0700 Subject: [PATCH 423/605] More fallout from #22737 and b130c433c94873c65701074a237d2af86a63a0fe --- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_all.txt b/requirements_all.txt index 7d154cd2ed7135..5720f84436d07f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1441,7 +1441,7 @@ pyvizio==0.0.4 pyvlx==0.2.10 # homeassistant.components.html5 -pywebpush==1.6.0 +pywebpush==1.9.2 # homeassistant.components.wemo pywemo==0.4.34 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8b26b7f4b93735..402f1f63ce8a3d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -260,7 +260,7 @@ pytradfri[async]==6.0.1 pyunifi==2.16 # homeassistant.components.html5 -pywebpush==1.6.0 +pywebpush==1.9.2 # homeassistant.components.rainmachine regenmaschine==1.4.0 From 563e4fbfca3b07b2fd77083642a7e145c05be868 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 5 Apr 2019 08:38:10 +0200 Subject: [PATCH 424/605] Add deprecation warning to embedded broker (#22753) --- homeassistant/components/mqtt/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 81d2dd8ea032c9..f77cd985a8a8d1 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -179,6 +179,16 @@ def validate_device_has_at_least_one_identifier(value: ConfigType) -> \ vol.Optional(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean, }, required=True) + +def embedded_broker_deprecated(value): + """Warn user that embedded MQTT broker is deprecated.""" + _LOGGER.warning( + "The embedded MQTT broker has been deprecated and will stop working" + "after June 5th, 2019. Use an external broker instead. For" + "instructions, see https://www.home-assistant.io/docs/mqtt/broker") + return value + + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_CLIENT_ID): cv.string, @@ -198,7 +208,8 @@ def validate_device_has_at_least_one_identifier(value: ConfigType) -> \ vol.Any('auto', '1.0', '1.1', '1.2'), vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])), - vol.Optional(CONF_EMBEDDED): HBMQTT_CONFIG_SCHEMA, + vol.Optional(CONF_EMBEDDED): + vol.All(HBMQTT_CONFIG_SCHEMA, embedded_broker_deprecated), vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean, From a75b151dfa97388b75ab6a3347901df15f6142ea Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Fri, 5 Apr 2019 02:39:19 -0400 Subject: [PATCH 425/605] fix flaky test (#22748) --- tests/components/stream/test_recorder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/stream/test_recorder.py b/tests/components/stream/test_recorder.py index 4e227e463b422d..8e4a69e28ff601 100644 --- a/tests/components/stream/test_recorder.py +++ b/tests/components/stream/test_recorder.py @@ -41,7 +41,7 @@ async def test_record_stream(hass, hass_client): stream.stop() - assert segments == 3 + assert segments > 1 async def test_recorder_timeout(hass, hass_client): From 8c657d4254a1f64f2b8efebef429d0e685fe0f26 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Fri, 5 Apr 2019 02:40:22 -0400 Subject: [PATCH 426/605] use the input stream codec as the template for the output streams (#22747) --- homeassistant/components/stream/worker.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 3ca8ac079e350d..0292fd305964a3 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -29,11 +29,7 @@ def create_stream_buffer(stream_output, video_stream, audio_frame): segment = io.BytesIO() output = av.open( segment, mode='w', format=stream_output.format) - vstream = output.add_stream( - stream_output.video_codec, video_stream.rate) - # Fix format - vstream.codec_context.format = \ - video_stream.codec_context.format + vstream = output.add_stream(template=video_stream) # Check if audio is requested astream = None if stream_output.audio_codec: From 82a1c0d0e8fecaea8eadd41bbe561758303ff835 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Fri, 5 Apr 2019 02:40:47 -0400 Subject: [PATCH 427/605] Update Foscam stream for newer models (#22744) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models * changing logic for success and return none * Update Foscam stream for newer models * change if to or --- homeassistant/components/foscam/camera.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 9852f617d01006..308e6e86ec8ffc 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -53,10 +53,11 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._media_port = None + self._rtsp_port = None result, response = self._foscam_session.get_port_info() if result == 0: - self._media_port = response['mediaPort'] + self._rtsp_port = response.get('rtspPort') or \ + response.get('mediaPort') def camera_image(self): """Return a still image response from the camera.""" @@ -71,19 +72,19 @@ def camera_image(self): @property def supported_features(self): """Return supported features.""" - if self._media_port: + if self._rtsp_port: return SUPPORT_STREAM return 0 @property def stream_source(self): """Return the stream source.""" - if self._media_port: + if self._rtsp_port: return 'rtsp://{}:{}@{}:{}/videoMain'.format( self._username, self._password, self._foscam_session.host, - self._media_port) + self._rtsp_port) return None @property From 71e120ce971373afd466c9027b11e55b2c3b6f73 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 5 Apr 2019 08:41:13 +0200 Subject: [PATCH 428/605] Fix chunk streaming (#22730) * Fix chunk streaming * Cleanup a print * Better error handling * Fix import order --- homeassistant/components/hassio/http.py | 1 - homeassistant/components/hassio/ingress.py | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index 7284004d72f9d4..a798d312c257e7 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -85,7 +85,6 @@ async def _command_proxy( "http://{}/{}".format(self._host, path), data=data, headers=headers, timeout=read_timeout ) - print(client.headers) # Simple request if int(client.headers.get(CONTENT_LENGTH, 0)) < 4194000: diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 49e949c5789239..27a7f05ec14883 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -1,21 +1,23 @@ """Hass.io Add-on ingress service.""" import asyncio -from ipaddress import ip_address +import logging import os +from ipaddress import ip_address from typing import Dict, Union import aiohttp -from aiohttp import web -from aiohttp import hdrs +from aiohttp import hdrs, web from aiohttp.web_exceptions import HTTPBadGateway from multidict import CIMultiDict -from homeassistant.core import callback from homeassistant.components.http import HomeAssistantView +from homeassistant.core import callback from homeassistant.helpers.typing import HomeAssistantType from .const import X_HASSIO, X_INGRESS_PATH +_LOGGER = logging.getLogger(__name__) + @callback def async_setup_ingress(hass: HomeAssistantType, host: str): @@ -54,8 +56,8 @@ async def _handle( # Request return await self._handle_request(request, token, path) - except aiohttp.ClientError: - pass + except aiohttp.ClientError as err: + _LOGGER.debug("Ingress error with %s / %s: %s", token, path, err) raise HTTPBadGateway() from None @@ -126,11 +128,11 @@ async def _handle_request( try: await response.prepare(request) - async for data in result.content: + async for data in result.content.iter_chunked(4096): await response.write(data) - except (aiohttp.ClientError, aiohttp.ClientPayloadError): - pass + except (aiohttp.ClientError, aiohttp.ClientPayloadError) as err: + _LOGGER.debug("Stream error %s / %s: %s", token, path, err) return response @@ -183,7 +185,7 @@ def _response_header(response: aiohttp.ClientResponse) -> Dict[str, str]: for name, value in response.headers.items(): if name in (hdrs.TRANSFER_ENCODING, hdrs.CONTENT_LENGTH, - hdrs.CONTENT_TYPE): + hdrs.CONTENT_TYPE, hdrs.CONTENT_ENCODING): continue headers[name] = value From 876b5fbe966ec88edd6d091b6c660102aecc5876 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 5 Apr 2019 08:48:41 +0200 Subject: [PATCH 429/605] fixes configuration flow #22706 (#22754) --- homeassistant/components/daikin/config_flow.py | 7 +++++-- tests/components/daikin/test_config_flow.py | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index 590b5a027387a7..3c5daac4653be9 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -38,9 +38,12 @@ async def _create_device(self, host): """Create device.""" from pydaikin.appliance import Appliance try: + device = Appliance( + host, + self.hass.helpers.aiohttp_client.async_get_clientsession(), + ) with async_timeout.timeout(10): - device = await self.hass.async_add_executor_job( - Appliance, host) + await device.init() except asyncio.TimeoutError: return self.async_abort(reason='device_timeout') except Exception: # pylint: disable=broad-except diff --git a/tests/components/daikin/test_config_flow.py b/tests/components/daikin/test_config_flow.py index f6b2f0b1d41990..fa288f6c2eff3c 100644 --- a/tests/components/daikin/test_config_flow.py +++ b/tests/components/daikin/test_config_flow.py @@ -24,9 +24,14 @@ def init_config_flow(hass): @pytest.fixture def mock_daikin(): - """Mock tellduslive.""" + """Mock pydaikin.""" + async def mock_daikin_init(): + """Mock the init function in pydaikin.""" + pass + with MockDependency('pydaikin.appliance') as mock_daikin_: mock_daikin_.Appliance().values.get.return_value = 'AABBCCDDEEFF' + mock_daikin_.Appliance().init = mock_daikin_init yield mock_daikin_ From 8eb93a8beae51b6c9ccc5728aab3972ed83e6fdd Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 4 Apr 2019 11:10:44 +0200 Subject: [PATCH 430/605] Change URL handling (#22713) --- homeassistant/components/hassio/ingress.py | 28 +++++++++++----------- tests/components/hassio/test_ingress.py | 10 ++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 04241f53de9c0b..9b62bb89c94f67 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -30,7 +30,7 @@ class HassIOIngress(HomeAssistantView): """Hass.io view to handle base part.""" name = "api:hassio:ingress" - url = "/api/hassio_ingress/{addon}/{path:.+}" + url = "/api/hassio_ingress/{token}/{path:.+}" requires_auth = False def __init__(self, host: str, websession: aiohttp.ClientSession): @@ -38,21 +38,21 @@ def __init__(self, host: str, websession: aiohttp.ClientSession): self._host = host self._websession = websession - def _create_url(self, addon: str, path: str) -> str: + def _create_url(self, token: str, path: str) -> str: """Create URL to service.""" - return "http://{}/addons/{}/web/{}".format(self._host, addon, path) + return "http://{}/ingress/{}/{}".format(self._host, token, path) async def _handle( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: """Route data to Hass.io ingress service.""" try: # Websocket if _is_websocket(request): - return await self._handle_websocket(request, addon, path) + return await self._handle_websocket(request, token, path) # Request - return await self._handle_request(request, addon, path) + return await self._handle_request(request, token, path) except aiohttp.ClientError: pass @@ -65,15 +65,15 @@ async def _handle( delete = _handle async def _handle_websocket( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> web.WebSocketResponse: """Ingress route for websocket.""" ws_server = web.WebSocketResponse() await ws_server.prepare(request) # Preparing - url = self._create_url(addon, path) - source_header = _init_header(request, addon) + url = self._create_url(token, path) + source_header = _init_header(request, token) # Support GET query if request.query_string: @@ -95,12 +95,12 @@ async def _handle_websocket( return ws_server async def _handle_request( - self, request: web.Request, addon: str, path: str + self, request: web.Request, token: str, path: str ) -> Union[web.Response, web.StreamResponse]: """Ingress route for request.""" - url = self._create_url(addon, path) + url = self._create_url(token, path) data = await request.read() - source_header = _init_header(request, addon) + source_header = _init_header(request, token) async with self._websession.request( request.method, url, headers=source_header, @@ -136,7 +136,7 @@ async def _handle_request( def _init_header( - request: web.Request, addon: str + request: web.Request, token: str ) -> Union[CIMultiDict, Dict[str, str]]: """Create initial header.""" headers = {} @@ -151,7 +151,7 @@ def _init_header( headers[X_HASSIO] = os.environ.get('HASSIO_TOKEN', "") # Ingress information - headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(addon) + headers[X_INGRESS_PATH] = "/api/hassio_ingress/{}".format(token) # Set X-Forwarded-For forward_for = request.headers.get(hdrs.X_FORWARDED_FOR) diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 4b72eda4c2596a..0dda69b2b17b6e 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -13,7 +13,7 @@ async def test_ingress_request_get( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.get("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.get( @@ -45,7 +45,7 @@ async def test_ingress_request_get( async def test_ingress_request_post( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.post("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.post("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.post( @@ -77,7 +77,7 @@ async def test_ingress_request_post( async def test_ingress_request_put( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.put("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.put("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.put( @@ -109,7 +109,7 @@ async def test_ingress_request_put( async def test_ingress_request_delete( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.delete("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.delete("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1]), text="test") resp = await hassio_client.delete( @@ -142,7 +142,7 @@ async def test_ingress_request_delete( async def test_ingress_websocket( hassio_client, build_type, aioclient_mock): """Test no auth needed for .""" - aioclient_mock.get("http://127.0.0.1/addons/{}/web/{}".format( + aioclient_mock.get("http://127.0.0.1/ingress/{}/{}".format( build_type[0], build_type[1])) # Ignore error because we can setup a full IO infrastructure From 193b608ee073736778f670f30bc212e977529404 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 4 Apr 2019 09:06:54 -0400 Subject: [PATCH 431/605] fix device class lookup for binary sensors (#22724) --- homeassistant/components/zha/core/const.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index b7f418253d8d80..a34a049fad2ef4 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -77,7 +77,6 @@ GENERIC = 'generic' UNKNOWN = 'unknown' OPENING = 'opening' -ZONE = 'zone' OCCUPANCY = 'occupancy' ACCELERATION = 'acceleration' @@ -90,7 +89,7 @@ COLOR_CHANNEL = 'light_color' FAN_CHANNEL = 'fan' LEVEL_CHANNEL = ATTR_LEVEL -ZONE_CHANNEL = 'ias_zone' +ZONE_CHANNEL = ZONE = 'ias_zone' ELECTRICAL_MEASUREMENT_CHANNEL = 'electrical_measurement' POWER_CONFIGURATION_CHANNEL = 'power' EVENT_RELAY_CHANNEL = 'event_relay' From ec07affe0dc93bb44e50647863e210ed2cb57d39 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 4 Apr 2019 17:43:18 +0200 Subject: [PATCH 432/605] Fix ingress routing with / (#22728) --- homeassistant/components/hassio/ingress.py | 2 +- tests/components/hassio/test_ingress.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 9b62bb89c94f67..49e949c5789239 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -30,7 +30,7 @@ class HassIOIngress(HomeAssistantView): """Hass.io view to handle base part.""" name = "api:hassio:ingress" - url = "/api/hassio_ingress/{token}/{path:.+}" + url = "/api/hassio_ingress/{token}/{path:.*}" requires_auth = False def __init__(self, host: str, websession: aiohttp.ClientSession): diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 0dda69b2b17b6e..343068375decdd 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -8,7 +8,8 @@ @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_get( hassio_client, build_type, aioclient_mock): @@ -40,7 +41,8 @@ async def test_ingress_request_get( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_post( hassio_client, build_type, aioclient_mock): @@ -72,7 +74,8 @@ async def test_ingress_request_post( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_put( hassio_client, build_type, aioclient_mock): @@ -104,7 +107,8 @@ async def test_ingress_request_put( @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), - ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5") + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") ]) async def test_ingress_request_delete( hassio_client, build_type, aioclient_mock): From 79facb82c6a9b4d29bdfee95b40f159e5a55d8e4 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 5 Apr 2019 08:41:13 +0200 Subject: [PATCH 433/605] Fix chunk streaming (#22730) * Fix chunk streaming * Cleanup a print * Better error handling * Fix import order --- homeassistant/components/hassio/http.py | 1 - homeassistant/components/hassio/ingress.py | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index 7284004d72f9d4..a798d312c257e7 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -85,7 +85,6 @@ async def _command_proxy( "http://{}/{}".format(self._host, path), data=data, headers=headers, timeout=read_timeout ) - print(client.headers) # Simple request if int(client.headers.get(CONTENT_LENGTH, 0)) < 4194000: diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 49e949c5789239..27a7f05ec14883 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -1,21 +1,23 @@ """Hass.io Add-on ingress service.""" import asyncio -from ipaddress import ip_address +import logging import os +from ipaddress import ip_address from typing import Dict, Union import aiohttp -from aiohttp import web -from aiohttp import hdrs +from aiohttp import hdrs, web from aiohttp.web_exceptions import HTTPBadGateway from multidict import CIMultiDict -from homeassistant.core import callback from homeassistant.components.http import HomeAssistantView +from homeassistant.core import callback from homeassistant.helpers.typing import HomeAssistantType from .const import X_HASSIO, X_INGRESS_PATH +_LOGGER = logging.getLogger(__name__) + @callback def async_setup_ingress(hass: HomeAssistantType, host: str): @@ -54,8 +56,8 @@ async def _handle( # Request return await self._handle_request(request, token, path) - except aiohttp.ClientError: - pass + except aiohttp.ClientError as err: + _LOGGER.debug("Ingress error with %s / %s: %s", token, path, err) raise HTTPBadGateway() from None @@ -126,11 +128,11 @@ async def _handle_request( try: await response.prepare(request) - async for data in result.content: + async for data in result.content.iter_chunked(4096): await response.write(data) - except (aiohttp.ClientError, aiohttp.ClientPayloadError): - pass + except (aiohttp.ClientError, aiohttp.ClientPayloadError) as err: + _LOGGER.debug("Stream error %s / %s: %s", token, path, err) return response @@ -183,7 +185,7 @@ def _response_header(response: aiohttp.ClientResponse) -> Dict[str, str]: for name, value in response.headers.items(): if name in (hdrs.TRANSFER_ENCODING, hdrs.CONTENT_LENGTH, - hdrs.CONTENT_TYPE): + hdrs.CONTENT_TYPE, hdrs.CONTENT_ENCODING): continue headers[name] = value From bab966fb2900a1b705aa5c0a84b649c72d447a29 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 4 Apr 2019 13:50:10 -0600 Subject: [PATCH 434/605] Fix incorrect "Unavailable" Ambient sensors (#22734) * Fix incorrect "Unavailable" Ambient sensors * Removed unnecessary cast --- homeassistant/components/ambient_station/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 2383d0119457b4..d0f74da4170d9c 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -417,9 +417,8 @@ def __init__( @property def available(self): """Return True if entity is available.""" - return bool( - self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get( - self._sensor_type)) + return self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get( + self._sensor_type) is not None @property def device_info(self): From 74a7d4117e4d22e83ef745749193e6466bbc50db Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 4 Apr 2019 13:43:21 -0600 Subject: [PATCH 435/605] Bump aioambient to 0.2.0 (#22736) --- homeassistant/components/ambient_station/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index d0f74da4170d9c..cb0a2067d9f170 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -20,7 +20,7 @@ ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE, TYPE_BINARY_SENSOR, TYPE_SENSOR) -REQUIREMENTS = ['aioambient==0.1.3'] +REQUIREMENTS = ['aioambient==0.2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index a773c9c8c31248..231637fef8d7fa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -97,7 +97,7 @@ abodepy==0.15.0 afsapi==0.0.4 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.2.0 # homeassistant.components.asuswrt aioasuswrt==1.1.21 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 27d96c5e606b2d..48390ff101aa4b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -35,7 +35,7 @@ PyTransportNSW==0.1.1 YesssSMS==0.2.3 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.2.0 # homeassistant.components.automatic.device_tracker aioautomatic==0.6.5 From dc185b994d5838ea87a581be13de171876bf5449 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Fri, 5 Apr 2019 02:40:47 -0400 Subject: [PATCH 436/605] Update Foscam stream for newer models (#22744) * Update Foscam to support stream source * Removing spaces and tabs * Changing to Python3-style string formatting * Adding '_media_port' to hopefully cover other models * changing logic for success and return none * Update Foscam stream for newer models * change if to or --- homeassistant/components/foscam/camera.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index b6f2162d57a9d9..1b4ee039053935 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -58,10 +58,11 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._media_port = None + self._rtsp_port = None result, response = self._foscam_session.get_port_info() if result == 0: - self._media_port = response['mediaPort'] + self._rtsp_port = response.get('rtspPort') or \ + response.get('mediaPort') def camera_image(self): """Return a still image response from the camera.""" @@ -76,19 +77,19 @@ def camera_image(self): @property def supported_features(self): """Return supported features.""" - if self._media_port: + if self._rtsp_port: return SUPPORT_STREAM return 0 @property def stream_source(self): """Return the stream source.""" - if self._media_port: + if self._rtsp_port: return 'rtsp://{}:{}@{}:{}/videoMain'.format( self._username, self._password, self._foscam_session.host, - self._media_port) + self._rtsp_port) return None @property From 5252c92670e3d364d7f6f6e11896713ccc1c8e82 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Fri, 5 Apr 2019 02:40:22 -0400 Subject: [PATCH 437/605] use the input stream codec as the template for the output streams (#22747) --- homeassistant/components/stream/worker.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 3ca8ac079e350d..0292fd305964a3 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -29,11 +29,7 @@ def create_stream_buffer(stream_output, video_stream, audio_frame): segment = io.BytesIO() output = av.open( segment, mode='w', format=stream_output.format) - vstream = output.add_stream( - stream_output.video_codec, video_stream.rate) - # Fix format - vstream.codec_context.format = \ - video_stream.codec_context.format + vstream = output.add_stream(template=video_stream) # Check if audio is requested astream = None if stream_output.audio_codec: From e9d55bf1c072366b272f393d114f8af54a1f573b Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 5 Apr 2019 08:48:41 +0200 Subject: [PATCH 438/605] fixes configuration flow #22706 (#22754) --- homeassistant/components/daikin/config_flow.py | 7 +++++-- tests/components/daikin/test_config_flow.py | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index 590b5a027387a7..3c5daac4653be9 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -38,9 +38,12 @@ async def _create_device(self, host): """Create device.""" from pydaikin.appliance import Appliance try: + device = Appliance( + host, + self.hass.helpers.aiohttp_client.async_get_clientsession(), + ) with async_timeout.timeout(10): - device = await self.hass.async_add_executor_job( - Appliance, host) + await device.init() except asyncio.TimeoutError: return self.async_abort(reason='device_timeout') except Exception: # pylint: disable=broad-except diff --git a/tests/components/daikin/test_config_flow.py b/tests/components/daikin/test_config_flow.py index f6b2f0b1d41990..fa288f6c2eff3c 100644 --- a/tests/components/daikin/test_config_flow.py +++ b/tests/components/daikin/test_config_flow.py @@ -24,9 +24,14 @@ def init_config_flow(hass): @pytest.fixture def mock_daikin(): - """Mock tellduslive.""" + """Mock pydaikin.""" + async def mock_daikin_init(): + """Mock the init function in pydaikin.""" + pass + with MockDependency('pydaikin.appliance') as mock_daikin_: mock_daikin_.Appliance().values.get.return_value = 'AABBCCDDEEFF' + mock_daikin_.Appliance().init = mock_daikin_init yield mock_daikin_ From cd3f51f7b12d0113110ddea3a778531db49e2525 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 4 Apr 2019 23:49:37 -0700 Subject: [PATCH 439/605] Bumped version to 0.91.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 36c61a51916258..6ece276307e5a8 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 4b877dd96ff17aaa884d56726cd399b435dcc57c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 5 Apr 2019 13:29:43 +0200 Subject: [PATCH 440/605] Cleanup cookie handling (#22757) --- homeassistant/components/hassio/ingress.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 27a7f05ec14883..91224c6f54da77 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -106,7 +106,7 @@ async def _handle_request( async with self._websession.request( request.method, url, headers=source_header, - params=request.query, data=data, cookies=request.cookies + params=request.query, data=data ) as result: headers = _response_header(result) @@ -145,7 +145,8 @@ def _init_header( # filter flags for name, value in request.headers.items(): - if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE, + hdrs.CONTENT_ENCODING): continue headers[name] = value From 9198047ad5b3decc1b2090f97350eea8d4a930c8 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 5 Apr 2019 13:29:43 +0200 Subject: [PATCH 441/605] Cleanup cookie handling (#22757) --- homeassistant/components/hassio/ingress.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 27a7f05ec14883..91224c6f54da77 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -106,7 +106,7 @@ async def _handle_request( async with self._websession.request( request.method, url, headers=source_header, - params=request.query, data=data, cookies=request.cookies + params=request.query, data=data ) as result: headers = _response_header(result) @@ -145,7 +145,8 @@ def _init_header( # filter flags for name, value in request.headers.items(): - if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE, + hdrs.CONTENT_ENCODING): continue headers[name] = value From 5e7fdb479b7ccb8381a538b5a47980819f9c8725 Mon Sep 17 00:00:00 2001 From: zewelor Date: Fri, 5 Apr 2019 13:32:46 +0200 Subject: [PATCH 442/605] Fix yeelight recorder warning (#22756) --- homeassistant/components/yeelight/__init__.py | 34 +--------------- homeassistant/components/yeelight/light.py | 39 +++++++++++++++++-- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index fb218a67698e44..99382bb1da9858 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -111,37 +111,6 @@ ] -def _transitions_config_parser(transitions): - """Parse transitions config into initialized objects.""" - import yeelight - - transition_objects = [] - for transition_config in transitions: - transition, params = list(transition_config.items())[0] - transition_objects.append(getattr(yeelight, transition)(*params)) - - return transition_objects - - -def _parse_custom_effects(effects_config): - import yeelight - - effects = {} - for config in effects_config: - params = config[CONF_FLOW_PARAMS] - action = yeelight.Flow.actions[params[ATTR_ACTION]] - transitions = _transitions_config_parser( - params[ATTR_TRANSITIONS]) - - effects[config[CONF_NAME]] = { - ATTR_COUNT: params[ATTR_COUNT], - ATTR_ACTION: action, - ATTR_TRANSITIONS: transitions - } - - return effects - - def setup(hass, config): """Set up the Yeelight bulbs.""" conf = config.get(DOMAIN, {}) @@ -192,9 +161,8 @@ def _setup_device(hass, hass_config, ipaddr, device_config): platform_config = device_config.copy() platform_config[CONF_HOST] = ipaddr - platform_config[CONF_CUSTOM_EFFECTS] = _parse_custom_effects( + platform_config[CONF_CUSTOM_EFFECTS] = \ hass_config.get(DOMAIN, {}).get(CONF_CUSTOM_EFFECTS, {}) - ) load_platform(hass, LIGHT_DOMAIN, DOMAIN, platform_config, hass_config) load_platform(hass, BINARY_SENSOR_DOMAIN, DOMAIN, platform_config, diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 92b668c6987aa6..912a4f99c92d15 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -7,7 +7,7 @@ from homeassistant.util.color import ( color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) -from homeassistant.const import CONF_HOST, ATTR_ENTITY_ID +from homeassistant.const import CONF_HOST, ATTR_ENTITY_ID, CONF_NAME from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_COLOR_TEMP, @@ -19,8 +19,8 @@ CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED, YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS, - YEELIGHT_FLOW_TRANSITION_SCHEMA, _transitions_config_parser, - ACTION_RECOVER) + YEELIGHT_FLOW_TRANSITION_SCHEMA, ACTION_RECOVER, CONF_FLOW_PARAMS, + ATTR_ACTION, ATTR_COUNT) DEPENDENCIES = ['yeelight'] @@ -81,6 +81,37 @@ EFFECT_STOP] +def _transitions_config_parser(transitions): + """Parse transitions config into initialized objects.""" + import yeelight + + transition_objects = [] + for transition_config in transitions: + transition, params = list(transition_config.items())[0] + transition_objects.append(getattr(yeelight, transition)(*params)) + + return transition_objects + + +def _parse_custom_effects(effects_config): + import yeelight + + effects = {} + for config in effects_config: + params = config[CONF_FLOW_PARAMS] + action = yeelight.Flow.actions[params[ATTR_ACTION]] + transitions = _transitions_config_parser( + params[ATTR_TRANSITIONS]) + + effects[config[CONF_NAME]] = { + ATTR_COUNT: params[ATTR_COUNT], + ATTR_ACTION: action, + ATTR_TRANSITIONS: transitions + } + + return effects + + def _cmd(func): """Define a wrapper to catch exceptions from the bulb.""" def _wrap(self, *args, **kwargs): @@ -109,7 +140,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): device = hass.data[DATA_YEELIGHT][discovery_info[CONF_HOST]] _LOGGER.debug("Adding %s", device.name) - custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] + custom_effects = _parse_custom_effects(discovery_info[CONF_CUSTOM_EFFECTS]) lights = [YeelightLight(device, custom_effects=custom_effects)] From 9eb32728f1069385dc425a4648a4881f3a321626 Mon Sep 17 00:00:00 2001 From: zewelor Date: Fri, 5 Apr 2019 13:32:46 +0200 Subject: [PATCH 443/605] Fix yeelight recorder warning (#22756) --- homeassistant/components/yeelight/__init__.py | 34 +--------------- homeassistant/components/yeelight/light.py | 39 +++++++++++++++++-- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index fb218a67698e44..99382bb1da9858 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -111,37 +111,6 @@ ] -def _transitions_config_parser(transitions): - """Parse transitions config into initialized objects.""" - import yeelight - - transition_objects = [] - for transition_config in transitions: - transition, params = list(transition_config.items())[0] - transition_objects.append(getattr(yeelight, transition)(*params)) - - return transition_objects - - -def _parse_custom_effects(effects_config): - import yeelight - - effects = {} - for config in effects_config: - params = config[CONF_FLOW_PARAMS] - action = yeelight.Flow.actions[params[ATTR_ACTION]] - transitions = _transitions_config_parser( - params[ATTR_TRANSITIONS]) - - effects[config[CONF_NAME]] = { - ATTR_COUNT: params[ATTR_COUNT], - ATTR_ACTION: action, - ATTR_TRANSITIONS: transitions - } - - return effects - - def setup(hass, config): """Set up the Yeelight bulbs.""" conf = config.get(DOMAIN, {}) @@ -192,9 +161,8 @@ def _setup_device(hass, hass_config, ipaddr, device_config): platform_config = device_config.copy() platform_config[CONF_HOST] = ipaddr - platform_config[CONF_CUSTOM_EFFECTS] = _parse_custom_effects( + platform_config[CONF_CUSTOM_EFFECTS] = \ hass_config.get(DOMAIN, {}).get(CONF_CUSTOM_EFFECTS, {}) - ) load_platform(hass, LIGHT_DOMAIN, DOMAIN, platform_config, hass_config) load_platform(hass, BINARY_SENSOR_DOMAIN, DOMAIN, platform_config, diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 92b668c6987aa6..912a4f99c92d15 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -7,7 +7,7 @@ from homeassistant.util.color import ( color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) -from homeassistant.const import CONF_HOST, ATTR_ENTITY_ID +from homeassistant.const import CONF_HOST, ATTR_ENTITY_ID, CONF_NAME from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_COLOR_TEMP, @@ -19,8 +19,8 @@ CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED, YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS, - YEELIGHT_FLOW_TRANSITION_SCHEMA, _transitions_config_parser, - ACTION_RECOVER) + YEELIGHT_FLOW_TRANSITION_SCHEMA, ACTION_RECOVER, CONF_FLOW_PARAMS, + ATTR_ACTION, ATTR_COUNT) DEPENDENCIES = ['yeelight'] @@ -81,6 +81,37 @@ EFFECT_STOP] +def _transitions_config_parser(transitions): + """Parse transitions config into initialized objects.""" + import yeelight + + transition_objects = [] + for transition_config in transitions: + transition, params = list(transition_config.items())[0] + transition_objects.append(getattr(yeelight, transition)(*params)) + + return transition_objects + + +def _parse_custom_effects(effects_config): + import yeelight + + effects = {} + for config in effects_config: + params = config[CONF_FLOW_PARAMS] + action = yeelight.Flow.actions[params[ATTR_ACTION]] + transitions = _transitions_config_parser( + params[ATTR_TRANSITIONS]) + + effects[config[CONF_NAME]] = { + ATTR_COUNT: params[ATTR_COUNT], + ATTR_ACTION: action, + ATTR_TRANSITIONS: transitions + } + + return effects + + def _cmd(func): """Define a wrapper to catch exceptions from the bulb.""" def _wrap(self, *args, **kwargs): @@ -109,7 +140,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): device = hass.data[DATA_YEELIGHT][discovery_info[CONF_HOST]] _LOGGER.debug("Adding %s", device.name) - custom_effects = discovery_info[CONF_CUSTOM_EFFECTS] + custom_effects = _parse_custom_effects(discovery_info[CONF_CUSTOM_EFFECTS]) lights = [YeelightLight(device, custom_effects=custom_effects)] From 323dc5b78a03d9121393544579a82d1c481783e2 Mon Sep 17 00:00:00 2001 From: carstenschroeder Date: Fri, 5 Apr 2019 17:14:44 +0200 Subject: [PATCH 444/605] Improve exception handling in ADS integration (#22627) * add exception handling * fix hound findings * improve logging * improve logging II * fix try..except to large --- homeassistant/components/ads/__init__.py | 37 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/ads/__init__.py b/homeassistant/components/ads/__init__.py index 5ab53e3acd2f87..92c6ecb3335147 100644 --- a/homeassistant/components/ads/__init__.py +++ b/homeassistant/components/ads/__init__.py @@ -160,28 +160,41 @@ def register_device(self, device): def write_by_name(self, name, value, plc_datatype): """Write a value to the device.""" + import pyads with self._lock: - return self._client.write_by_name(name, value, plc_datatype) + try: + return self._client.write_by_name(name, value, plc_datatype) + except pyads.ADSError as err: + _LOGGER.error("Error writing %s: %s", name, err) def read_by_name(self, name, plc_datatype): """Read a value from the device.""" + import pyads with self._lock: - return self._client.read_by_name(name, plc_datatype) + try: + return self._client.read_by_name(name, plc_datatype) + except pyads.ADSError as err: + _LOGGER.error("Error reading %s: %s", name, err) def add_device_notification(self, name, plc_datatype, callback): """Add a notification to the ADS devices.""" - from pyads import NotificationAttrib - attr = NotificationAttrib(ctypes.sizeof(plc_datatype)) + import pyads + attr = pyads.NotificationAttrib(ctypes.sizeof(plc_datatype)) with self._lock: - hnotify, huser = self._client.add_device_notification( - name, attr, self._device_notification_callback) - hnotify = int(hnotify) - self._notification_items[hnotify] = NotificationItem( - hnotify, huser, name, plc_datatype, callback) - - _LOGGER.debug( - "Added device notification %d for variable %s", hnotify, name) + try: + hnotify, huser = self._client.add_device_notification( + name, attr, self._device_notification_callback) + except pyads.ADSError as err: + _LOGGER.error("Error subscribing to %s: %s", name, err) + else: + hnotify = int(hnotify) + self._notification_items[hnotify] = NotificationItem( + hnotify, huser, name, plc_datatype, callback) + + _LOGGER.debug( + "Added device notification %d for variable %s", + hnotify, name) def _device_notification_callback(self, notification, name): """Handle device notifications.""" From 879967bed216be6dc4b58cb8b0f1eafa9c89be56 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Fri, 5 Apr 2019 09:15:35 -0700 Subject: [PATCH 445/605] Correctly load Mopar's config (#22771) --- homeassistant/components/mopar/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mopar/__init__.py b/homeassistant/components/mopar/__init__.py index d845d585765ef2..4ee9f3219b483a 100644 --- a/homeassistant/components/mopar/__init__.py +++ b/homeassistant/components/mopar/__init__.py @@ -53,12 +53,13 @@ def setup(hass, config): """Set up the Mopar component.""" import motorparts + conf = config[DOMAIN] cookie = hass.config.path(COOKIE_FILE) try: session = motorparts.get_session( - config[CONF_USERNAME], - config[CONF_PASSWORD], - config[CONF_PIN], + conf[CONF_USERNAME], + conf[CONF_PASSWORD], + conf[CONF_PIN], cookie_path=cookie ) except motorparts.MoparError: @@ -69,7 +70,7 @@ def setup(hass, config): data.update(now=None) track_time_interval( - hass, data.update, config[CONF_SCAN_INTERVAL] + hass, data.update, conf[CONF_SCAN_INTERVAL] ) def handle_horn(call): From b3e60df82aec2116040a2814cd3c9d09aef320ed Mon Sep 17 00:00:00 2001 From: teliov Date: Fri, 5 Apr 2019 19:11:04 +0200 Subject: [PATCH 446/605] Add google hangouts manual authentication option (#22158) * Added option to use manual authentication for google hangout component See: https://hangups.readthedocs.io/en/latest/user_guide.html#logging-in for manual log in example Bumped up version of hangups to 0.4.9 * Updated components/hangouts/strings.json and generated translation string by running script/translations_develop Reduced verbosity of modifications to components/hangouts/config_flow.py * Added option to use manual authentication for google hangout component See: https://hangups.readthedocs.io/en/latest/user_guide.html#logging-in for manual log in example Bumped up version of hangups to 0.4.9 * Updated components/hangouts/strings.json and generated translation string by running script/translations_develop Reduced verbosity of modifications to components/hangouts/config_flow.py * fixing missing rebase --- .../components/hangouts/.translations/en.json | 1 + homeassistant/components/hangouts/__init__.py | 2 +- .../components/hangouts/config_flow.py | 29 ++++++++++++++----- homeassistant/components/hangouts/const.py | 1 + .../components/hangouts/hangups_utils.py | 17 ++++++++++- .../components/hangouts/strings.json | 3 +- tests/components/hangouts/test_config_flow.py | 14 +++++++++ 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/hangouts/.translations/en.json b/homeassistant/components/hangouts/.translations/en.json index f526bec4f34679..31e5f9894f96fc 100644 --- a/homeassistant/components/hangouts/.translations/en.json +++ b/homeassistant/components/hangouts/.translations/en.json @@ -18,6 +18,7 @@ }, "user": { "data": { + "authorization_code": "Authorization Code (required for manual authentication)", "email": "E-Mail Address", "password": "Password" }, diff --git a/homeassistant/components/hangouts/__init__.py b/homeassistant/components/hangouts/__init__.py index 2d36de8b7692bf..29cdc29e5ada6c 100644 --- a/homeassistant/components/hangouts/__init__.py +++ b/homeassistant/components/hangouts/__init__.py @@ -19,7 +19,7 @@ MESSAGE_SCHEMA, SERVICE_RECONNECT, SERVICE_SEND_MESSAGE, SERVICE_UPDATE, TARGETS_SCHEMA) -REQUIREMENTS = ['hangups==0.4.6'] +REQUIREMENTS = ['hangups==0.4.9'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hangouts/config_flow.py b/homeassistant/components/hangouts/config_flow.py index 5eecc24d45e96b..743c49abfdf5d6 100644 --- a/homeassistant/components/hangouts/config_flow.py +++ b/homeassistant/components/hangouts/config_flow.py @@ -1,11 +1,14 @@ """Config flow to configure Google Hangouts.""" +import functools import voluptuous as vol + from homeassistant import config_entries from homeassistant.const import CONF_EMAIL, CONF_PASSWORD from homeassistant.core import callback -from .const import CONF_2FA, CONF_REFRESH_TOKEN, DOMAIN as HANGOUTS_DOMAIN +from .const import CONF_2FA, CONF_REFRESH_TOKEN, CONF_AUTH_CODE, \ + DOMAIN as HANGOUTS_DOMAIN @callback @@ -41,13 +44,24 @@ async def async_step_user(self, user_input=None): from .hangups_utils import (HangoutsCredentials, HangoutsRefreshToken, GoogleAuthError, Google2FAError) - self._credentials = HangoutsCredentials(user_input[CONF_EMAIL], - user_input[CONF_PASSWORD]) + user_email = user_input[CONF_EMAIL] + user_password = user_input[CONF_PASSWORD] + user_auth_code = user_input.get(CONF_AUTH_CODE) + manual_login = user_auth_code is not None + + user_pin = None + self._credentials = HangoutsCredentials(user_email, + user_password, + user_pin, + user_auth_code) self._refresh_token = HangoutsRefreshToken(None) try: - await self.hass.async_add_executor_job(get_auth, - self._credentials, - self._refresh_token) + await self.hass.async_add_executor_job( + functools.partial(get_auth, + self._credentials, + self._refresh_token, + manual_login=manual_login) + ) return await self.async_step_final() except GoogleAuthError as err: @@ -63,7 +77,8 @@ async def async_step_user(self, user_input=None): step_id='user', data_schema=vol.Schema({ vol.Required(CONF_EMAIL): str, - vol.Required(CONF_PASSWORD): str + vol.Required(CONF_PASSWORD): str, + vol.Optional(CONF_AUTH_CODE): str }), errors=errors ) diff --git a/homeassistant/components/hangouts/const.py b/homeassistant/components/hangouts/const.py index 38b238292b3333..f664e769b9ffa4 100644 --- a/homeassistant/components/hangouts/const.py +++ b/homeassistant/components/hangouts/const.py @@ -13,6 +13,7 @@ DOMAIN = 'hangouts' CONF_2FA = '2fa' +CONF_AUTH_CODE = 'authorization_code' CONF_REFRESH_TOKEN = 'refresh_token' CONF_BOT = 'bot' diff --git a/homeassistant/components/hangouts/hangups_utils.py b/homeassistant/components/hangouts/hangups_utils.py index 9aff7730201ac9..d2556ac15a08cd 100644 --- a/homeassistant/components/hangouts/hangups_utils.py +++ b/homeassistant/components/hangouts/hangups_utils.py @@ -13,7 +13,7 @@ class HangoutsCredentials(CredentialsPrompt): This implementation gets the user data as params. """ - def __init__(self, email, password, pin=None): + def __init__(self, email, password, pin=None, auth_code=None): """Google account credentials. :param email: Google account email address. @@ -23,6 +23,7 @@ def __init__(self, email, password, pin=None): self._email = email self._password = password self._pin = pin + self._auth_code = auth_code def get_email(self): """Return email. @@ -54,6 +55,20 @@ def set_verification_code(self, pin): """ self._pin = pin + def get_authorization_code(self): + """Return the oauth authorization code. + + :return: Google oauth code. + """ + return self._auth_code + + def set_authorization_code(self, code): + """Set the google oauth authorization code. + + :param code: Oauth code returned after authentication with google. + """ + self._auth_code = code + class HangoutsRefreshToken(RefreshTokenCache): """Memory-based cache for refresh token.""" diff --git a/homeassistant/components/hangouts/strings.json b/homeassistant/components/hangouts/strings.json index c83a0ae08764d8..8c155784ebe1c0 100644 --- a/homeassistant/components/hangouts/strings.json +++ b/homeassistant/components/hangouts/strings.json @@ -13,7 +13,8 @@ "user": { "data": { "email": "E-Mail Address", - "password": "Password" + "password": "Password", + "authorization_code": "Authorization Code (required for manual authentication)" }, "title": "Google Hangouts Login" }, diff --git a/tests/components/hangouts/test_config_flow.py b/tests/components/hangouts/test_config_flow.py index af9bb018919500..becb981d68da35 100644 --- a/tests/components/hangouts/test_config_flow.py +++ b/tests/components/hangouts/test_config_flow.py @@ -19,6 +19,20 @@ async def test_flow_works(hass, aioclient_mock): assert result['title'] == 'test@test.com' +async def test_flow_works_with_authcode(hass, aioclient_mock): + """Test config flow without 2fa.""" + flow = config_flow.HangoutsFlowHandler() + + flow.hass = hass + + with patch('hangups.get_auth'): + result = await flow.async_step_user( + {'email': 'test@test.com', 'password': '1232456', + 'authorization_code': 'c29tZXJhbmRvbXN0cmluZw=='}) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'test@test.com' + + async def test_flow_works_with_2fa(hass, aioclient_mock): """Test config flow with 2fa.""" from homeassistant.components.hangouts.hangups_utils import Google2FAError From 008b641c564d48c0d3a80491d8b5572a12c32b7f Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 5 Apr 2019 19:14:54 +0200 Subject: [PATCH 447/605] Axis - support stream (#22593) * Add support for new stream component --- homeassistant/components/axis/camera.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index ec1d761d3d00d0..62b694a99bb971 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -1,5 +1,6 @@ """Support for Axis camera streaming.""" +from homeassistant.components.camera import SUPPORT_STREAM from homeassistant.components.mjpeg.camera import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera, filter_urllib3_logging) from homeassistant.const import ( @@ -14,6 +15,7 @@ AXIS_IMAGE = 'http://{}:{}/axis-cgi/jpg/image.cgi' AXIS_VIDEO = 'http://{}:{}/axis-cgi/mjpg/video.cgi' +AXIS_STREAM = 'rtsp://{}:{}@{}/axis-media/media.amp?videocodec=h264' async def async_setup_entry(hass, config_entry, async_add_entities): @@ -56,6 +58,19 @@ async def async_added_to_hass(self): self.unsub_dispatcher.append(async_dispatcher_connect( self.hass, self.device.event_reachable, self.update_callback)) + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + return AXIS_STREAM.format( + self.device.config_entry.data[CONF_DEVICE][CONF_USERNAME], + self.device.config_entry.data[CONF_DEVICE][CONF_PASSWORD], + self.device.host) + @callback def update_callback(self, no_delay=None): """Update the cameras state.""" From 7a33dc5cec0fe864c0857c570c89cbfbb6502401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Osb=C3=A4ck?= Date: Fri, 5 Apr 2019 21:22:24 +0200 Subject: [PATCH 448/605] update core dependencies due to pywebpush update (#22767) --- homeassistant/package_constraints.txt | 4 ++-- requirements_all.txt | 4 ++-- setup.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 04704a0048493c..5b0673038bb1bc 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -5,8 +5,8 @@ attrs==18.2.0 bcrypt==3.1.6 certifi>=2018.04.16 jinja2>=2.10 -PyJWT==1.6.4 -cryptography==2.5 +PyJWT==1.7.1 +cryptography==2.6.1 pip>=8.0.3 python-slugify==1.2.6 pytz>=2018.07 diff --git a/requirements_all.txt b/requirements_all.txt index 5720f84436d07f..64b7b4aa072614 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,8 +6,8 @@ attrs==18.2.0 bcrypt==3.1.6 certifi>=2018.04.16 jinja2>=2.10 -PyJWT==1.6.4 -cryptography==2.5 +PyJWT==1.7.1 +cryptography==2.6.1 pip>=8.0.3 python-slugify==1.2.6 pytz>=2018.07 diff --git a/setup.py b/setup.py index 8f7e84dd8d8c48..0245923afb1615 100755 --- a/setup.py +++ b/setup.py @@ -39,9 +39,9 @@ 'bcrypt==3.1.6', 'certifi>=2018.04.16', 'jinja2>=2.10', - 'PyJWT==1.6.4', + 'PyJWT==1.7.1', # PyJWT has loose dependency. We want the latest one. - 'cryptography==2.5', + 'cryptography==2.6.1', 'pip>=8.0.3', 'python-slugify==1.2.6', 'pytz>=2018.07', From d1bf4708997483fc15a1f5d32a1b56ac9f5f7e1d Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 5 Apr 2019 22:21:06 +0200 Subject: [PATCH 449/605] deCONZ multiple gateways fixup (#22774) * Initial PR was merged before a proper review was performed * These fixes follow Martins review comments after merge --- homeassistant/components/deconz/__init__.py | 4 +--- tests/components/deconz/test_init.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index ff1ee2bf06e3e5..807f82821fb293 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -4,7 +4,6 @@ from homeassistant import config_entries from homeassistant.const import ( CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP) -from homeassistant.core import callback from homeassistant.helpers import config_validation as cv # Loading the config flow file will register the flow @@ -51,7 +50,7 @@ async def async_setup(hass, config): """ if not hass.config_entries.async_entries(DOMAIN) and DOMAIN in config: deconz_config = config[DOMAIN] - hass.async_add_job(hass.config_entries.flow.async_init( + hass.async_create_task(hass.config_entries.flow.async_init( DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, data=deconz_config )) @@ -175,7 +174,6 @@ async def async_unload_entry(hass, config_entry): return await gateway.async_reset() -@callback async def async_populate_options(hass, config_entry): """Populate default options for gateway. diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index da37f4a9652006..b844cf4336ed68 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -41,7 +41,7 @@ async def test_config_with_host_passed_to_config_entry(hass): } }) is True # Import flow started - assert len(mock_config_flow.mock_calls) == 2 + assert len(mock_config_flow.mock_calls) == 1 async def test_config_without_host_not_passed_to_config_entry(hass): From 144632a81b9b9409afafa6f884904317f59ffc3b Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Fri, 5 Apr 2019 18:22:57 -0400 Subject: [PATCH 450/605] Fix konnected unique_id computation for switches (#22777) --- homeassistant/components/konnected/switch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index 7384d62900eec9..3db602215b968d 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -41,9 +41,10 @@ def __init__(self, device_id, pin_num, data): self._pause = self._data.get(CONF_PAUSE) self._repeat = self._data.get(CONF_REPEAT) self._state = self._boolean_state(self._data.get(ATTR_STATE)) - self._unique_id = '{}-{}'.format(device_id, hash(frozenset( - {self._pin_num, self._momentary, self._pause, self._repeat}))) self._name = self._data.get(CONF_NAME) + self._unique_id = '{}-{}-{}-{}-{}'.format( + device_id, self._pin_num, self._momentary, + self._pause, self._repeat) @property def unique_id(self) -> str: From 8dfbfae270f457d2f45dfc10d3628321e21cf636 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Fri, 5 Apr 2019 19:06:41 -0400 Subject: [PATCH 451/605] ZHA Light debug logging. (#22776) --- homeassistant/components/zha/light.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 573936d6ac2a8c..feccbc09663943 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -167,6 +167,7 @@ async def async_turn_on(self, **kwargs): duration = transition * 10 if transition else DEFAULT_DURATION brightness = kwargs.get(light.ATTR_BRIGHTNESS) + t_log = {} if (brightness is not None or transition) and \ self._supported_features & light.SUPPORT_BRIGHTNESS: if brightness is not None: @@ -177,7 +178,9 @@ async def async_turn_on(self, **kwargs): level, duration ) + t_log['move_to_level_with_on_off'] = success if not success: + self.debug("turned on: %s", t_log) return self._state = bool(level) if level: @@ -185,7 +188,9 @@ async def async_turn_on(self, **kwargs): if brightness is None or brightness: success = await self._on_off_channel.on() + t_log['on_off'] = success if not success: + self.debug("turned on: %s", t_log) return self._state = True @@ -194,7 +199,9 @@ async def async_turn_on(self, **kwargs): temperature = kwargs[light.ATTR_COLOR_TEMP] success = await self._color_channel.move_to_color_temp( temperature, duration) + t_log['move_to_color_temp'] = success if not success: + self.debug("turned on: %s", t_log) return self._color_temp = temperature @@ -207,10 +214,13 @@ async def async_turn_on(self, **kwargs): int(xy_color[1] * 65535), duration, ) + t_log['move_to_color'] = success if not success: + self.debug("turned on: %s", t_log) return self._hs_color = hs_color + self.debug("turned on: %s", t_log) self.async_schedule_update_ha_state() async def async_turn_off(self, **kwargs): @@ -224,7 +234,7 @@ async def async_turn_off(self, **kwargs): ) else: success = await self._on_off_channel.off() - _LOGGER.debug("%s was turned off: %s", self.entity_id, success) + self.debug("turned off: %s", success) if not success: return self._state = False @@ -243,3 +253,7 @@ async def async_update(self): async def refresh(self, time): """Call async_update at an interval.""" await self.async_update() + + def debug(self, msg, *args): + """Log debug message.""" + _LOGGER.debug('%s: ' + msg, self.entity_id, *args) From 192ed90773c9c605ff3323224d57fb1ae74585da Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 5 Apr 2019 19:50:20 -0400 Subject: [PATCH 452/605] make the custom polling actually request state (#22778) --- homeassistant/components/zha/light.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index feccbc09663943..cebc18e6a3eb84 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -243,16 +243,20 @@ async def async_turn_off(self, **kwargs): async def async_update(self): """Attempt to retrieve on off state from the light.""" await super().async_update() + await self.async_get_state() + + async def async_get_state(self, from_cache=True): + """Attempt to retrieve on off state from the light.""" if self._on_off_channel: self._state = await self._on_off_channel.get_attribute_value( - 'on_off') + 'on_off', from_cache=from_cache) if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( - 'current_level') + 'current_level', from_cache=from_cache) async def refresh(self, time): - """Call async_update at an interval.""" - await self.async_update() + """Call async_get_state at an interval.""" + await self.async_get_state(from_cache=False) def debug(self, msg, *args): """Log debug message.""" From a44966f483795e3e723ae11065423d2e83927265 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Fri, 5 Apr 2019 09:15:35 -0700 Subject: [PATCH 453/605] Correctly load Mopar's config (#22771) --- homeassistant/components/mopar/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mopar/__init__.py b/homeassistant/components/mopar/__init__.py index d845d585765ef2..4ee9f3219b483a 100644 --- a/homeassistant/components/mopar/__init__.py +++ b/homeassistant/components/mopar/__init__.py @@ -53,12 +53,13 @@ def setup(hass, config): """Set up the Mopar component.""" import motorparts + conf = config[DOMAIN] cookie = hass.config.path(COOKIE_FILE) try: session = motorparts.get_session( - config[CONF_USERNAME], - config[CONF_PASSWORD], - config[CONF_PIN], + conf[CONF_USERNAME], + conf[CONF_PASSWORD], + conf[CONF_PIN], cookie_path=cookie ) except motorparts.MoparError: @@ -69,7 +70,7 @@ def setup(hass, config): data.update(now=None) track_time_interval( - hass, data.update, config[CONF_SCAN_INTERVAL] + hass, data.update, conf[CONF_SCAN_INTERVAL] ) def handle_horn(call): From dbe53a39474405552250650cbf6472b969c0ba4a Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Fri, 5 Apr 2019 18:22:57 -0400 Subject: [PATCH 454/605] Fix konnected unique_id computation for switches (#22777) --- homeassistant/components/konnected/switch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index 7384d62900eec9..3db602215b968d 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -41,9 +41,10 @@ def __init__(self, device_id, pin_num, data): self._pause = self._data.get(CONF_PAUSE) self._repeat = self._data.get(CONF_REPEAT) self._state = self._boolean_state(self._data.get(ATTR_STATE)) - self._unique_id = '{}-{}'.format(device_id, hash(frozenset( - {self._pin_num, self._momentary, self._pause, self._repeat}))) self._name = self._data.get(CONF_NAME) + self._unique_id = '{}-{}-{}-{}-{}'.format( + device_id, self._pin_num, self._momentary, + self._pause, self._repeat) @property def unique_id(self) -> str: From f004f440d3e04cb073ea1d36fa1a9552bb478c67 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 5 Apr 2019 19:50:20 -0400 Subject: [PATCH 455/605] make the custom polling actually request state (#22778) --- homeassistant/components/zha/light.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 6ba4efa9b0f874..27a8e37e3abe66 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -238,13 +238,21 @@ async def async_turn_off(self, **kwargs): async def async_update(self): """Attempt to retrieve on off state from the light.""" await super().async_update() + await self.async_get_state() + + async def async_get_state(self, from_cache=True): + """Attempt to retrieve on off state from the light.""" if self._on_off_channel: self._state = await self._on_off_channel.get_attribute_value( - 'on_off') + 'on_off', from_cache=from_cache) if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( - 'current_level') + 'current_level', from_cache=from_cache) async def refresh(self, time): - """Call async_update at an interval.""" - await self.async_update() + """Call async_get_state at an interval.""" + await self.async_get_state(from_cache=False) + + def debug(self, msg, *args): + """Log debug message.""" + _LOGGER.debug('%s: ' + msg, self.entity_id, *args) From a69b1a359da0e288c670979125f1531bed107fcb Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Fri, 5 Apr 2019 19:06:41 -0400 Subject: [PATCH 456/605] ZHA Light debug logging. (#22776) --- homeassistant/components/zha/light.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 27a8e37e3abe66..fc29fd0cdd2ef0 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -172,6 +172,7 @@ async def async_turn_on(self, **kwargs): duration = transition * 10 if transition else DEFAULT_DURATION brightness = kwargs.get(light.ATTR_BRIGHTNESS) + t_log = {} if (brightness is not None or transition) and \ self._supported_features & light.SUPPORT_BRIGHTNESS: if brightness is not None: @@ -182,7 +183,9 @@ async def async_turn_on(self, **kwargs): level, duration ) + t_log['move_to_level_with_on_off'] = success if not success: + self.debug("turned on: %s", t_log) return self._state = bool(level) if level: @@ -190,7 +193,9 @@ async def async_turn_on(self, **kwargs): if brightness is None or brightness: success = await self._on_off_channel.on() + t_log['on_off'] = success if not success: + self.debug("turned on: %s", t_log) return self._state = True @@ -199,7 +204,9 @@ async def async_turn_on(self, **kwargs): temperature = kwargs[light.ATTR_COLOR_TEMP] success = await self._color_channel.move_to_color_temp( temperature, duration) + t_log['move_to_color_temp'] = success if not success: + self.debug("turned on: %s", t_log) return self._color_temp = temperature @@ -212,10 +219,13 @@ async def async_turn_on(self, **kwargs): int(xy_color[1] * 65535), duration, ) + t_log['move_to_color'] = success if not success: + self.debug("turned on: %s", t_log) return self._hs_color = hs_color + self.debug("turned on: %s", t_log) self.async_schedule_update_ha_state() async def async_turn_off(self, **kwargs): @@ -229,7 +239,7 @@ async def async_turn_off(self, **kwargs): ) else: success = await self._on_off_channel.off() - _LOGGER.debug("%s was turned off: %s", self.entity_id, success) + self.debug("turned off: %s", success) if not success: return self._state = False From 2b490e44862ca7daae113fff092202ce9a646823 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Fri, 5 Apr 2019 23:02:38 -0400 Subject: [PATCH 457/605] Add optional rtsp_port for Foscam (#22786) * add optional rtsp port for config * getting rid of default=None * removing vol.Any --- homeassistant/components/foscam/camera.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 308e6e86ec8ffc..6ce8f1865fcf77 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -14,6 +14,7 @@ REQUIREMENTS = ['libpyfoscam==1.0'] CONF_IP = 'ip' +CONF_RTSP_PORT = 'rtsp_port' DEFAULT_NAME = 'Foscam Camera' DEFAULT_PORT = 88 @@ -26,6 +27,7 @@ vol.Required(CONF_USERNAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_RTSP_PORT): cv.port }) @@ -53,11 +55,12 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._rtsp_port = None - result, response = self._foscam_session.get_port_info() - if result == 0: - self._rtsp_port = response.get('rtspPort') or \ - response.get('mediaPort') + self._rtsp_port = device_info.get(CONF_RTSP_PORT) + if not self._rtsp_port: + result, response = self._foscam_session.get_port_info() + if result == 0: + self._rtsp_port = response.get('rtspPort') or \ + response.get('mediaPort') def camera_image(self): """Return a still image response from the camera.""" From 8b3cf2d493c308d397845b70bc839a91a70bbbef Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 6 Apr 2019 12:09:15 +0200 Subject: [PATCH 458/605] Update homeassistant-pyozw 0.1.4 (#22794) --- homeassistant/components/zwave/__init__.py | 2 +- homeassistant/components/zwave/manifest.json | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 4abaaa312109b8..aca36aabd3b4ac 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -37,7 +37,7 @@ from .util import (check_node_schema, check_value_schema, node_name, check_has_unique_id, is_node_parsed) -REQUIREMENTS = ['pydispatcher==2.0.5', 'homeassistant-pyozw==0.1.3'] +REQUIREMENTS = ['pydispatcher==2.0.5', 'homeassistant-pyozw==0.1.4'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zwave/manifest.json b/homeassistant/components/zwave/manifest.json index ac7e327f19aa40..598af58ad177fc 100644 --- a/homeassistant/components/zwave/manifest.json +++ b/homeassistant/components/zwave/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave", "documentation": "https://www.home-assistant.io/components/zwave", "requirements": [ - "homeassistant-pyozw==0.1.3", + "homeassistant-pyozw==0.1.4", "pydispatcher==2.0.5" ], "dependencies": [], diff --git a/requirements_all.txt b/requirements_all.txt index 64b7b4aa072614..2effe22f37b9c1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -542,7 +542,7 @@ holidays==0.9.10 home-assistant-frontend==20190331.0 # homeassistant.components.zwave -homeassistant-pyozw==0.1.3 +homeassistant-pyozw==0.1.4 # homeassistant.components.homekit_controller homekit[IP]==0.13.0 From 6351c5c6ab953b7e87c4a9263e514bd3fd796b3c Mon Sep 17 00:00:00 2001 From: panosmz Date: Sat, 6 Apr 2019 16:20:51 +0300 Subject: [PATCH 459/605] Add OASA Telematics greek public transport sensor component (#22196) * add telematics sensor * add missing final newline * code cleanup & add manifest * fixes from review * fix flake8 warning * rerun gen_requirements_all.py script --- .coveragerc | 1 + .../components/oasa_telematics/__init__.py | 1 + .../components/oasa_telematics/manifest.json | 10 + .../components/oasa_telematics/sensor.py | 192 ++++++++++++++++++ requirements_all.txt | 3 + 5 files changed, 207 insertions(+) create mode 100644 homeassistant/components/oasa_telematics/__init__.py create mode 100644 homeassistant/components/oasa_telematics/manifest.json create mode 100644 homeassistant/components/oasa_telematics/sensor.py diff --git a/.coveragerc b/.coveragerc index 957b3402c46f9e..5eac18b8d7e072 100644 --- a/.coveragerc +++ b/.coveragerc @@ -398,6 +398,7 @@ omit = homeassistant/components/nzbget/sensor.py homeassistant/components/octoprint/* homeassistant/components/oem/climate.py + homeassistant/components/oasa_telematics/sensor.py homeassistant/components/ohmconnect/sensor.py homeassistant/components/onewire/sensor.py homeassistant/components/onkyo/media_player.py diff --git a/homeassistant/components/oasa_telematics/__init__.py b/homeassistant/components/oasa_telematics/__init__.py new file mode 100644 index 00000000000000..3629f31982b121 --- /dev/null +++ b/homeassistant/components/oasa_telematics/__init__.py @@ -0,0 +1 @@ +"""The OASA Telematics component.""" diff --git a/homeassistant/components/oasa_telematics/manifest.json b/homeassistant/components/oasa_telematics/manifest.json new file mode 100644 index 00000000000000..15bf40e63c865c --- /dev/null +++ b/homeassistant/components/oasa_telematics/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "oasa_telematics", + "name": "OASA Telematics", + "documentation": "https://www.home-assistant.io/components/oasa_telematics/", + "requirements": [ + "oasatelematics==0.3" + ], + "dependencies": [], + "codeowners": [] +} \ No newline at end of file diff --git a/homeassistant/components/oasa_telematics/sensor.py b/homeassistant/components/oasa_telematics/sensor.py new file mode 100644 index 00000000000000..665f2f83f86af7 --- /dev/null +++ b/homeassistant/components/oasa_telematics/sensor.py @@ -0,0 +1,192 @@ +"""Support for OASA Telematics from telematics.oasa.gr.""" +import logging +from datetime import timedelta +from operator import itemgetter + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_NAME, ATTR_ATTRIBUTION, DEVICE_CLASS_TIMESTAMP) +from homeassistant.helpers.entity import Entity +from homeassistant.util import dt as dt_util + +REQUIREMENTS = ['oasatelematics==0.3'] +_LOGGER = logging.getLogger(__name__) + +ATTR_STOP_ID = 'stop_id' +ATTR_STOP_NAME = 'stop_name' +ATTR_ROUTE_ID = 'route_id' +ATTR_ROUTE_NAME = 'route_name' +ATTR_NEXT_ARRIVAL = 'next_arrival' +ATTR_SECOND_NEXT_ARRIVAL = 'second_next_arrival' +ATTR_NEXT_DEPARTURE = 'next_departure' + +ATTRIBUTION = "Data retrieved from telematics.oasa.gr" + +CONF_STOP_ID = 'stop_id' +CONF_ROUTE_ID = 'route_id' + +DEFAULT_NAME = 'OASA Telematics' +ICON = 'mdi:bus' + +SCAN_INTERVAL = timedelta(seconds=60) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_STOP_ID): cv.string, + vol.Required(CONF_ROUTE_ID): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the OASA Telematics sensor.""" + name = config[CONF_NAME] + stop_id = config[CONF_STOP_ID] + route_id = config.get(CONF_ROUTE_ID) + + data = OASATelematicsData(stop_id, route_id) + + add_entities([OASATelematicsSensor( + data, stop_id, route_id, name)], True) + + +class OASATelematicsSensor(Entity): + """Implementation of the OASA Telematics sensor.""" + + def __init__(self, data, stop_id, route_id, name): + """Initialize the sensor.""" + self.data = data + self._name = name + self._stop_id = stop_id + self._route_id = route_id + self._name_data = self._times = self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def device_class(self): + """Return the class of this sensor.""" + return DEVICE_CLASS_TIMESTAMP + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def device_state_attributes(self): + """Return the state attributes.""" + params = {} + if self._times is not None: + next_arrival_data = self._times[0] + if ATTR_NEXT_ARRIVAL in next_arrival_data: + next_arrival = next_arrival_data[ATTR_NEXT_ARRIVAL] + params.update({ + ATTR_NEXT_ARRIVAL: next_arrival.isoformat() + }) + if len(self._times) > 1: + second_next_arrival_time = self._times[1][ATTR_NEXT_ARRIVAL] + if second_next_arrival_time is not None: + second_arrival = second_next_arrival_time + params.update({ + ATTR_SECOND_NEXT_ARRIVAL: second_arrival.isoformat() + }) + params.update({ + ATTR_ROUTE_ID: self._times[0][ATTR_ROUTE_ID], + ATTR_STOP_ID: self._stop_id, + ATTR_ATTRIBUTION: ATTRIBUTION, + }) + params.update({ + ATTR_ROUTE_NAME: self._name_data[ATTR_ROUTE_NAME], + ATTR_STOP_NAME: self._name_data[ATTR_STOP_NAME] + }) + return {k: v for k, v in params.items() if v} + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return ICON + + def update(self): + """Get the latest data from OASA API and update the states.""" + self.data.update() + self._times = self.data.info + self._name_data = self.data.name_data + next_arrival_data = self._times[0] + if ATTR_NEXT_ARRIVAL in next_arrival_data: + self._state = next_arrival_data[ATTR_NEXT_ARRIVAL].isoformat() + + +class OASATelematicsData(): + """The class for handling data retrieval.""" + + def __init__(self, stop_id, route_id): + """Initialize the data object.""" + import oasatelematics + self.stop_id = stop_id + self.route_id = route_id + self.info = self.empty_result() + self.oasa_api = oasatelematics + self.name_data = {ATTR_ROUTE_NAME: self.get_route_name(), + ATTR_STOP_NAME: self.get_stop_name()} + + def empty_result(self): + """Object returned when no arrivals are found.""" + return [{ATTR_ROUTE_ID: self.route_id}] + + def get_route_name(self): + """Get the route name from the API.""" + try: + route = self.oasa_api.getRouteName(self.route_id) + if route: + return route[0].get('route_departure_eng') + except TypeError: + _LOGGER.error("Cannot get route name from OASA API") + return None + + def get_stop_name(self): + """Get the stop name from the API.""" + try: + name_data = self.oasa_api.getStopNameAndXY(self.stop_id) + if name_data: + return name_data[0].get('stop_descr_matrix_eng') + except TypeError: + _LOGGER.error("Cannot get stop name from OASA API") + return None + + def update(self): + """Get the latest arrival data from telematics.oasa.gr API.""" + self.info = [] + + results = self.oasa_api.getStopArrivals(self.stop_id) + + if not results: + self.info = self.empty_result() + return + + # Parse results + results = [r for r in results if r.get('route_code') in self.route_id] + current_time = dt_util.utcnow() + + for result in results: + btime2 = result.get('btime2') + if btime2 is not None: + arrival_min = int(btime2) + timestamp = current_time + timedelta(minutes=arrival_min) + arrival_data = {ATTR_NEXT_ARRIVAL: timestamp, + ATTR_ROUTE_ID: self.route_id} + self.info.append(arrival_data) + + if not self.info: + _LOGGER.debug("No arrivals with given parameters") + self.info = self.empty_result() + return + + # Sort the data by time + sort = sorted(self.info, itemgetter(ATTR_NEXT_ARRIVAL)) + self.info = sort diff --git a/requirements_all.txt b/requirements_all.txt index 2effe22f37b9c1..757994485a0a93 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -754,6 +754,9 @@ nuheat==0.3.0 # homeassistant.components.trend numpy==1.16.2 +# homeassistant.components.oasa_telematics +oasatelematics==0.3 + # homeassistant.components.google oauth2client==4.0.0 From a747eaa3ba0546ec886b4e58646f544689df5707 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Sat, 6 Apr 2019 08:18:50 -0700 Subject: [PATCH 460/605] Remove pycryptodome requirement for Android TV (#22552) * Bump androidtv to 0.0.15 * Bump androidtv to 0.0.15 in manifest.json --- .../components/androidtv/manifest.json | 2 +- .../components/androidtv/media_player.py | 24 +++++++++---------- requirements_all.txt | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/androidtv/manifest.json b/homeassistant/components/androidtv/manifest.json index 815a97394cb505..841ad299785825 100644 --- a/homeassistant/components/androidtv/manifest.json +++ b/homeassistant/components/androidtv/manifest.json @@ -3,7 +3,7 @@ "name": "Androidtv", "documentation": "https://www.home-assistant.io/components/androidtv", "requirements": [ - "androidtv==0.0.14" + "androidtv==0.0.15" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index a62a7f2a6d978f..706ef6f8402430 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -18,7 +18,7 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.14'] +REQUIREMENTS = ['androidtv==0.0.15'] _LOGGER = logging.getLogger(__name__) @@ -294,12 +294,12 @@ def turn_off(self): @adb_decorator() def media_previous_track(self): """Send previous track command (results in rewind).""" - self.aftv.media_previous() + self.aftv.media_previous_track() @adb_decorator() def media_next_track(self): """Send next track command (results in fast-forward).""" - self.aftv.media_next() + self.aftv.media_next_track() @adb_decorator() def adb_command(self, cmd): @@ -324,11 +324,11 @@ def __init__(self, aftv, name, apps, turn_on_command, turn_off_command) self._device = None - self._muted = None self._device_properties = self.aftv.device_properties + self._is_volume_muted = None self._unique_id = 'androidtv-{}-{}'.format( name, self._device_properties['serialno']) - self._volume = None + self._volume_level = None @adb_decorator(override_available=True) def update(self): @@ -345,16 +345,16 @@ def update(self): if not self._available: return - # Get the `state`, `current_app`, and `running_apps`. - state, self._current_app, self._device, self._muted, self._volume = \ - self.aftv.update() + # Get the updated state and attributes. + state, self._current_app, self._device, self._is_volume_muted, \ + self._volume_level = self.aftv.update() self._state = ANDROIDTV_STATES[state] @property def is_volume_muted(self): """Boolean if volume is currently muted.""" - return self._muted + return self._is_volume_muted @property def source(self): @@ -374,7 +374,7 @@ def unique_id(self): @property def volume_level(self): """Return the volume level.""" - return self._volume + return self._volume_level @adb_decorator() def media_stop(self): @@ -389,12 +389,12 @@ def mute_volume(self, mute): @adb_decorator() def volume_down(self): """Send volume down command.""" - self.aftv.volume_down() + self._volume_level = self.aftv.volume_down(self._volume_level) @adb_decorator() def volume_up(self): """Send volume up command.""" - self.aftv.volume_up() + self._volume_level = self.aftv.volume_up(self._volume_level) class FireTVDevice(ADBDevice): diff --git a/requirements_all.txt b/requirements_all.txt index 757994485a0a93..4493911e106b29 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -158,7 +158,7 @@ alpha_vantage==2.1.0 amcrest==1.3.0 # homeassistant.components.androidtv -androidtv==0.0.14 +androidtv==0.0.15 # homeassistant.components.anel_pwrctrl anel_pwrctrl-homeassistant==0.0.1.dev2 From 87cabc933c9481530048033f99d7cfd1cd9a5bb9 Mon Sep 17 00:00:00 2001 From: Andrew Hayworth Date: Sat, 6 Apr 2019 20:55:15 -0500 Subject: [PATCH 461/605] Update version of python_awair to 0.0.4 (#22809) The awair API has changed again, this time substituting 'lat' and 'lon' for 'latitude' and 'longitude'. --- homeassistant/components/awair/manifest.json | 2 +- homeassistant/components/awair/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/awair/manifest.json b/homeassistant/components/awair/manifest.json index bc63ef06cc2f5b..cba11e8be1ca00 100644 --- a/homeassistant/components/awair/manifest.json +++ b/homeassistant/components/awair/manifest.json @@ -3,7 +3,7 @@ "name": "Awair", "documentation": "https://www.home-assistant.io/components/awair", "requirements": [ - "python_awair==0.0.3" + "python_awair==0.0.4" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/awair/sensor.py b/homeassistant/components/awair/sensor.py index 5b199538e68079..7fdcc6735495c1 100644 --- a/homeassistant/components/awair/sensor.py +++ b/homeassistant/components/awair/sensor.py @@ -15,7 +15,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle, dt -REQUIREMENTS = ['python_awair==0.0.3'] +REQUIREMENTS = ['python_awair==0.0.4'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 4493911e106b29..d91af2c43cbf5d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1393,7 +1393,7 @@ python-whois==0.7.1 python-wink==1.10.3 # homeassistant.components.awair -python_awair==0.0.3 +python_awair==0.0.4 # homeassistant.components.swiss_public_transport python_opendata_transport==0.1.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 402f1f63ce8a3d..54b6230ae8115e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -251,7 +251,7 @@ python-forecastio==1.4.0 python-nest==4.1.0 # homeassistant.components.awair -python_awair==0.0.3 +python_awair==0.0.4 # homeassistant.components.tradfri pytradfri[async]==6.0.1 From 55619da7228740d9b78ce988a166436271f09868 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 05:31:21 +0200 Subject: [PATCH 462/605] Remove unused group status (#22791) --- homeassistant/components/cast/media_player.py | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index c4019b4686c7be..4b2972b0c002eb 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -406,13 +406,10 @@ def removed_from_multizone(self, group_uuid): """Handle the cast removed from a group.""" if self._valid: self._cast_device.multizone_new_media_status(group_uuid, None) - self._cast_device.multizone_new_cast_status(group_uuid, None) def multizone_new_cast_status(self, group_uuid, cast_status): - """Handle reception of a new MediaStatus for a group.""" - if self._valid: - self._cast_device.multizone_new_cast_status( - group_uuid, cast_status) + """Handle reception of a new CastStatus for a group.""" + pass def multizone_new_media_status(self, group_uuid, media_status): """Handle reception of a new MediaStatus for a group.""" @@ -456,8 +453,7 @@ def __init__(self, cast_device, chromecast, mz_mgr): def new_cast_status(self, cast_status): """Handle reception of a new CastStatus.""" - if self._valid: - self._cast_device.new_dynamic_group_cast_status(cast_status) + pass def new_media_status(self, media_status): """Handle reception of a new MediaStatus.""" @@ -502,10 +498,8 @@ def __init__(self, cast_info): self._dynamic_group_cast_info = None # type: ChromecastInfo self._dynamic_group_cast = None \ # type: Optional[pychromecast.Chromecast] - self.dynamic_group_cast_status = None self.dynamic_group_media_status = None self.dynamic_group_media_status_received = None - self.mz_cast_status = {} self.mz_media_status = {} self.mz_media_status_received = {} self.mz_mgr = None @@ -685,7 +679,6 @@ async def async_set_dynamic_group(self, cast_info): self._dynamic_group_status_listener = DynamicGroupCastStatusListener( self, chromecast, mz_mgr) self._dynamic_group_available = False - self.dynamic_group_cast_status = chromecast.status self.dynamic_group_media_status = chromecast.media_controller.status self._dynamic_group_cast.start() self.async_schedule_update_ha_state() @@ -734,7 +727,6 @@ def _invalidate(self): self.cast_status = None self.media_status = None self.media_status_received = None - self.mz_cast_status = {} self.mz_media_status = {} self.mz_media_status_received = {} self.mz_mgr = None @@ -745,7 +737,6 @@ def _invalidate(self): def _dynamic_group_invalidate(self): """Invalidate some attributes.""" self._dynamic_group_cast = None - self.dynamic_group_cast_status = None self.dynamic_group_media_status = None self.dynamic_group_media_status_received = None if self._dynamic_group_status_listener is not None: @@ -797,11 +788,6 @@ def new_connection_status(self, connection_status): self._available = new_available self.schedule_update_ha_state() - def new_dynamic_group_cast_status(self, cast_status): - """Handle updates of the cast status.""" - self.dynamic_group_cast_status = cast_status - self.schedule_update_ha_state() - def new_dynamic_group_media_status(self, media_status): """Handle updates of the media status.""" self.dynamic_group_media_status = media_status @@ -848,16 +834,6 @@ def multizone_new_media_status(self, group_uuid, media_status): self.mz_media_status_received[group_uuid] = dt_util.utcnow() self.schedule_update_ha_state() - def multizone_new_cast_status(self, group_uuid, cast_status): - """Handle updates of audio group status.""" - _LOGGER.debug( - "[%s %s (%s:%s)] Multizone %s cast status: %s", - self.entity_id, self._cast_info.friendly_name, - self._cast_info.host, self._cast_info.port, - group_uuid, cast_status) - self.mz_cast_status[group_uuid] = cast_status - self.schedule_update_ha_state() - # ========== Service Calls ========== def _media_controller(self): """ From 353fca3b6eca478bbaf7bbf3475f735f9896ef8a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 05:31:39 +0200 Subject: [PATCH 463/605] Raise severity of MQTT callback deprecation warning (#22792) --- homeassistant/components/mqtt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index f77cd985a8a8d1..9af0465c0de306 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -371,7 +371,7 @@ async def async_subscribe(hass: HomeAssistantType, topic: str, wrapped_msg_callback = msg_callback # If we have 3 paramaters with no default value, wrap the callback if non_default == 3: - _LOGGER.info( + _LOGGER.warning( "Signature of MQTT msg_callback '%s.%s' is deprecated", inspect.getmodule(msg_callback).__name__, msg_callback.__name__) wrapped_msg_callback = wrap_msg_callback(msg_callback) From 8c17b2f7dd6990e26ab2264e5a836c17b7efae68 Mon Sep 17 00:00:00 2001 From: Justin Vanderhooft Date: Sat, 6 Apr 2019 23:33:28 -0400 Subject: [PATCH 464/605] Bump raincloud dependency to fix broken integration: Fixes #22422 (#22805) * Bump raincloud dependency to fix broken integration: Fixes #22422 * bump requirements_all * bump CODEOWNERS * edit codeowners in response to PR feedback --- CODEOWNERS | 1 + homeassistant/components/raincloud/__init__.py | 2 +- homeassistant/components/raincloud/manifest.json | 4 ++-- requirements_all.txt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5adc4d071c2c6f..a5c06f991c9a2a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -162,6 +162,7 @@ homeassistant/components/pvoutput/* @fabaff homeassistant/components/qnap/* @colinodell homeassistant/components/quantum_gateway/* @cisasteelersfan homeassistant/components/qwikswitch/* @kellerza +homeassistant/components/raincloud/* @vanstinator homeassistant/components/rainmachine/* @bachya homeassistant/components/random/* @fabaff homeassistant/components/rfxtrx/* @danielhiversen diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py index 473d52bdbdd119..c31729197be3fe 100644 --- a/homeassistant/components/raincloud/__init__.py +++ b/homeassistant/components/raincloud/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['raincloudy==0.0.5'] +REQUIREMENTS = ['raincloudy==0.0.6'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/raincloud/manifest.json b/homeassistant/components/raincloud/manifest.json index 2befec24b91a3d..dddfc6f156ae34 100644 --- a/homeassistant/components/raincloud/manifest.json +++ b/homeassistant/components/raincloud/manifest.json @@ -3,8 +3,8 @@ "name": "Raincloud", "documentation": "https://www.home-assistant.io/components/raincloud", "requirements": [ - "raincloudy==0.0.5" + "raincloudy==0.0.6" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@vanstinator"] } diff --git a/requirements_all.txt b/requirements_all.txt index d91af2c43cbf5d..265ff9de860b8b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1471,7 +1471,7 @@ rachiopy==0.1.3 radiotherm==2.0.0 # homeassistant.components.raincloud -raincloudy==0.0.5 +raincloudy==0.0.6 # homeassistant.components.raspihats # raspihats==2.2.3 From c8eebb6b4a5fbefec0f4fc6621bf9466e34de6b6 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Sun, 7 Apr 2019 09:43:07 +0200 Subject: [PATCH 465/605] Add HmIP-SMO to Homematic IP (#22802) --- .../components/homematicip_cloud/binary_sensor.py | 7 ++++--- homeassistant/components/homematicip_cloud/sensor.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 071b4a0a3fbe8b..44c17282dda03d 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -30,9 +30,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the HomematicIP Cloud binary sensor from a config entry.""" from homematicip.aio.device import ( AsyncDevice, AsyncShutterContact, AsyncMotionDetectorIndoor, - AsyncSmokeDetector, AsyncWaterSensor, AsyncRotaryHandleSensor, - AsyncMotionDetectorPushButton, AsyncWeatherSensor, - AsyncWeatherSensorPlus, AsyncWeatherSensorPro) + AsyncMotionDetectorOutdoor, AsyncSmokeDetector, AsyncWaterSensor, + AsyncRotaryHandleSensor, AsyncMotionDetectorPushButton, + AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro) from homematicip.aio.group import ( AsyncSecurityGroup, AsyncSecurityZoneGroup) @@ -43,6 +43,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): if isinstance(device, (AsyncShutterContact, AsyncRotaryHandleSensor)): devices.append(HomematicipShutterContact(home, device)) if isinstance(device, (AsyncMotionDetectorIndoor, + AsyncMotionDetectorOutdoor, AsyncMotionDetectorPushButton)): devices.append(HomematicipMotionDetector(home, device)) if isinstance(device, AsyncSmokeDetector): diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 2038433df4fa80..5f345f419fac09 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -28,7 +28,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): AsyncHeatingThermostat, AsyncHeatingThermostatCompact, AsyncTemperatureHumiditySensorWithoutDisplay, AsyncTemperatureHumiditySensorDisplay, AsyncMotionDetectorIndoor, - AsyncTemperatureHumiditySensorOutdoor, + AsyncMotionDetectorOutdoor, AsyncTemperatureHumiditySensorOutdoor, AsyncMotionDetectorPushButton, AsyncLightSensor, AsyncPlugableSwitchMeasuring, AsyncBrandSwitchMeasuring, AsyncFullFlushSwitchMeasuring, AsyncWeatherSensor, @@ -49,6 +49,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): devices.append(HomematicipTemperatureSensor(home, device)) devices.append(HomematicipHumiditySensor(home, device)) if isinstance(device, (AsyncMotionDetectorIndoor, + AsyncMotionDetectorOutdoor, AsyncMotionDetectorPushButton, AsyncWeatherSensor, AsyncWeatherSensorPlus, From 3ce6be62979f84e17454d1de98a8985ca4c4f2ca Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 01:16:54 -0700 Subject: [PATCH 466/605] Add a new mobile_app webhook command to get config (#22813) * Add a new mobile_app webhook command to get config * Limit fields returned --- homeassistant/components/mobile_app/const.py | 6 ++-- .../components/mobile_app/webhook.py | 24 ++++++++++--- tests/components/mobile_app/test_webhook.py | 34 +++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index b59c631ba9936d..52ca0aa39930c6 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,6 +67,7 @@ WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_GET_CONFIG = 'get_config' WEBHOOK_TYPE_GET_ZONES = 'get_zones' WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' @@ -75,8 +76,9 @@ WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_GET_CONFIG, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES] diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 71c6d0d66734e0..a57d3930f50544 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -8,7 +8,7 @@ ATTR_DEV_ID, DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) - +from homeassistant.components.frontend import MANIFEST_JSON from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, @@ -36,9 +36,9 @@ ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES, - WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_CONFIG, + WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -273,3 +273,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, in sorted(hass.states.async_entity_ids(ZONE_DOMAIN))) return webhook_response(list(zones), registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_GET_CONFIG: + + hass_config = hass.config.as_dict() + + return webhook_response({ + 'latitude': hass_config['latitude'], + 'longitude': hass_config['longitude'], + 'elevation': hass_config['elevation'], + 'unit_system': hass_config['unit_system'], + 'location_name': hass_config['location_name'], + 'time_zone': hass_config['time_zone'], + 'components': hass_config['components'], + 'version': hass_config['version'], + 'theme_color': MANIFEST_JSON['theme_color'], + }, registration=registration, headers=headers) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index ad19a70a806ac2..43eac28ec18421 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -126,6 +126,40 @@ async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F40 assert json[0]['entity_id'] == 'zone.home' +async def test_webhook_handle_get_config(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 + """Test that we can get config properly.""" + resp = await webhook_client.post( + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), + json={'type': 'get_config'} + ) + + assert resp.status == 200 + + json = await resp.json() + if 'components' in json: + json['components'] = set(json['components']) + if 'whitelist_external_dirs' in json: + json['whitelist_external_dirs'] = \ + set(json['whitelist_external_dirs']) + + hass_config = hass.config.as_dict() + + expected_dict = { + 'latitude': hass_config['latitude'], + 'longitude': hass_config['longitude'], + 'elevation': hass_config['elevation'], + 'unit_system': hass_config['unit_system'], + 'location_name': hass_config['location_name'], + 'time_zone': hass_config['time_zone'], + 'components': hass_config['components'], + 'version': hass_config['version'], + 'theme_color': '#03A9F4', # Default frontend theme color + } + + assert expected_dict == json + + async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 create_registrations, # noqa: F401, F811, E501 caplog): # noqa: E501 F811 From 6492809a7eb91a278676e34edd0027a2b26e4462 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 01:17:14 -0700 Subject: [PATCH 467/605] Fix for optional values in the update_location webhook call (#22817) * Fix for optional values in the update_location webhook call * Square brackets instead of .get --- .../components/mobile_app/webhook.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index a57d3930f50544..7a83ba4e978f34 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -145,18 +145,26 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION: see_payload = { ATTR_DEV_ID: registration[ATTR_DEVICE_ID], - ATTR_LOCATION_NAME: data.get(ATTR_LOCATION_NAME), - ATTR_GPS: data.get(ATTR_GPS), - ATTR_GPS_ACCURACY: data.get(ATTR_GPS_ACCURACY), - ATTR_BATTERY: data.get(ATTR_BATTERY), - ATTR_ATTRIBUTES: { - ATTR_SPEED: data.get(ATTR_SPEED), - ATTR_ALTITUDE: data.get(ATTR_ALTITUDE), - ATTR_COURSE: data.get(ATTR_COURSE), - ATTR_VERTICAL_ACCURACY: data.get(ATTR_VERTICAL_ACCURACY), - } + ATTR_GPS: data[ATTR_GPS], + ATTR_GPS_ACCURACY: data[ATTR_GPS_ACCURACY], } + for key in (ATTR_LOCATION_NAME, ATTR_BATTERY): + value = data.get(key) + if value is not None: + see_payload[key] = value + + attrs = {} + + for key in (ATTR_ALTITUDE, ATTR_COURSE, + ATTR_SPEED, ATTR_VERTICAL_ACCURACY): + value = data.get(key) + if value is not None: + attrs[key] = value + + if attrs: + see_payload[ATTR_ATTRIBUTES] = attrs + try: await hass.services.async_call(DT_DOMAIN, DT_SEE, see_payload, From 83fb3637d9cac18a135756fc53680acfc9d707ec Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 15:56:38 +0200 Subject: [PATCH 468/605] Sort configuration schema. (#22835) --- homeassistant/components/mqtt/lock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index e01a30f0fab065..b9c095a054aeeb 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -30,14 +30,14 @@ DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_LOCK, default=DEFAULT_PAYLOAD_LOCK): cv.string, vol.Optional(CONF_PAYLOAD_UNLOCK, default=DEFAULT_PAYLOAD_UNLOCK): cv.string, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) From 842534d472b887268ad6f3bae9f72bff666d70dc Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:00:40 +0200 Subject: [PATCH 469/605] Use dict[key] for required config keys and keys with default values. (#22831) --- homeassistant/components/mqtt/climate.py | 156 +++++++++++------------ 1 file changed, 76 insertions(+), 80 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 52d18a034194ca..0f4229c8688219 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -66,11 +66,11 @@ CONF_FAN_MODE_LIST = 'fan_modes' CONF_MODE_LIST = 'modes' CONF_SWING_MODE_LIST = 'swing_modes' -CONF_INITIAL = 'initial' CONF_SEND_IF_OFF = 'send_if_off' -CONF_MIN_TEMP = 'min_temp' -CONF_MAX_TEMP = 'max_temp' +CONF_TEMP_INITIAL = 'initial' +CONF_TEMP_MIN = 'min_temp' +CONF_TEMP_MAX = 'max_temp' CONF_TEMP_STEP = 'temp_step' TEMPLATE_KEYS = ( @@ -87,57 +87,53 @@ SCHEMA_BASE = CLIMATE_PLATFORM_SCHEMA.extend(MQTT_BASE_PLATFORM_SCHEMA.schema) PLATFORM_SCHEMA = SCHEMA_BASE.extend({ - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, - vol.Optional(CONF_POWER_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_TEMPERATURE_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic, - - vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template, vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic, - - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_POWER_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_TEMPERATURE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_SWING_MODE_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_AWAY_MODE_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_CURRENT_TEMPERATURE_TEMPLATE): cv.template, - vol.Optional(CONF_CURRENT_TEMPERATURE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_FAN_MODE_LIST, default=[STATE_AUTO, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list, - vol.Optional(CONF_SWING_MODE_LIST, - default=[STATE_ON, STATE_OFF]): cv.ensure_list, + vol.Optional(CONF_FAN_MODE_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_HOLD_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_MODE_LIST, default=[STATE_AUTO, STATE_OFF, STATE_COOL, STATE_HEAT, STATE_DRY, STATE_FAN_ONLY]): cv.ensure_list, + vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_INITIAL, default=21): cv.positive_int, - vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean, vol.Optional(CONF_PAYLOAD_ON, default="ON"): cv.string, vol.Optional(CONF_PAYLOAD_OFF, default="OFF"): cv.string, - - vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float), - vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float), + vol.Optional(CONF_POWER_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_POWER_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean, + vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SWING_MODE_LIST, + default=[STATE_ON, STATE_OFF]): cv.ensure_list, + vol.Optional(CONF_SWING_MODE_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_TEMP_INITIAL, default=21): cv.positive_int, + vol.Optional(CONF_TEMP_MIN, default=DEFAULT_MIN_TEMP): vol.Coerce(float), + vol.Optional(CONF_TEMP_MAX, default=DEFAULT_MAX_TEMP): vol.Coerce(float), vol.Optional(CONF_TEMP_STEP, default=1.0): vol.Coerce(float), + vol.Optional(CONF_TEMPERATURE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TEMPERATURE_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -255,7 +251,7 @@ def _setup_from_config(self, config): self._target_temperature = self._current_fan_mode = \ self._current_operation = self._current_swing_mode = None if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is None: - self._target_temperature = config.get(CONF_INITIAL) + self._target_temperature = config[CONF_TEMP_INITIAL] if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: self._current_fan_mode = SPEED_LOW if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: @@ -279,7 +275,7 @@ def _setup_from_config(self, config): async def _subscribe_topics(self): """(Re)Subscribe to topics.""" topics = {} - qos = self._config.get(CONF_QOS) + qos = self._config[CONF_QOS] @callback def handle_current_temp_received(msg): @@ -310,7 +306,7 @@ def handle_mode_received(msg): payload = self._value_templates[CONF_MODE_STATE_TEMPLATE].\ async_render_with_possible_json_value(payload) - if payload not in self._config.get(CONF_MODE_LIST): + if payload not in self._config[CONF_MODE_LIST]: _LOGGER.error("Invalid mode: %s", payload) else: self._current_operation = payload @@ -352,7 +348,7 @@ def handle_fan_mode_received(msg): self._value_templates[CONF_FAN_MODE_STATE_TEMPLATE].\ async_render_with_possible_json_value(payload) - if payload not in self._config.get(CONF_FAN_MODE_LIST): + if payload not in self._config[CONF_FAN_MODE_LIST]: _LOGGER.error("Invalid fan mode: %s", payload) else: self._current_fan_mode = payload @@ -373,7 +369,7 @@ def handle_swing_mode_received(msg): self._value_templates[CONF_SWING_MODE_STATE_TEMPLATE].\ async_render_with_possible_json_value(payload) - if payload not in self._config.get(CONF_SWING_MODE_LIST): + if payload not in self._config[CONF_SWING_MODE_LIST]: _LOGGER.error("Invalid swing mode: %s", payload) else: self._current_swing_mode = payload @@ -389,8 +385,8 @@ def handle_swing_mode_received(msg): def handle_away_mode_received(msg): """Handle receiving away mode via MQTT.""" payload = msg.payload - payload_on = self._config.get(CONF_PAYLOAD_ON) - payload_off = self._config.get(CONF_PAYLOAD_OFF) + payload_on = self._config[CONF_PAYLOAD_ON] + payload_off = self._config[CONF_PAYLOAD_OFF] if CONF_AWAY_MODE_STATE_TEMPLATE in self._value_templates: payload = \ self._value_templates[CONF_AWAY_MODE_STATE_TEMPLATE].\ @@ -419,8 +415,8 @@ def handle_away_mode_received(msg): def handle_aux_mode_received(msg): """Handle receiving aux mode via MQTT.""" payload = msg.payload - payload_on = self._config.get(CONF_PAYLOAD_ON) - payload_off = self._config.get(CONF_PAYLOAD_OFF) + payload_on = self._config[CONF_PAYLOAD_ON] + payload_off = self._config[CONF_PAYLOAD_OFF] if CONF_AUX_STATE_TEMPLATE in self._value_templates: payload = self._value_templates[CONF_AUX_STATE_TEMPLATE].\ async_render_with_possible_json_value(payload) @@ -480,7 +476,7 @@ def should_poll(self): @property def name(self): """Return the name of the climate device.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -510,12 +506,12 @@ def current_operation(self): @property def operation_list(self): """Return the list of available operation modes.""" - return self._config.get(CONF_MODE_LIST) + return self._config[CONF_MODE_LIST] @property def target_temperature_step(self): """Return the supported step of target temperature.""" - return self._config.get(CONF_TEMP_STEP) + return self._config[CONF_TEMP_STEP] @property def is_away_mode_on(self): @@ -540,7 +536,7 @@ def current_fan_mode(self): @property def fan_list(self): """Return the list of available fan modes.""" - return self._config.get(CONF_FAN_MODE_LIST) + return self._config[CONF_FAN_MODE_LIST] async def async_set_temperature(self, **kwargs): """Set new target temperatures.""" @@ -553,24 +549,24 @@ async def async_set_temperature(self, **kwargs): # optimistic mode self._target_temperature = kwargs.get(ATTR_TEMPERATURE) - if (self._config.get(CONF_SEND_IF_OFF) or + if (self._config[CONF_SEND_IF_OFF] or self._current_operation != STATE_OFF): mqtt.async_publish( self.hass, self._topic[CONF_TEMPERATURE_COMMAND_TOPIC], - kwargs.get(ATTR_TEMPERATURE), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + kwargs.get(ATTR_TEMPERATURE), self._config[CONF_QOS], + self._config[CONF_RETAIN]) # Always optimistic? self.async_write_ha_state() async def async_set_swing_mode(self, swing_mode): """Set new swing mode.""" - if (self._config.get(CONF_SEND_IF_OFF) or + if (self._config[CONF_SEND_IF_OFF] or self._current_operation != STATE_OFF): mqtt.async_publish( self.hass, self._topic[CONF_SWING_MODE_COMMAND_TOPIC], - swing_mode, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + swing_mode, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: self._current_swing_mode = swing_mode @@ -578,12 +574,12 @@ async def async_set_swing_mode(self, swing_mode): async def async_set_fan_mode(self, fan_mode): """Set new target temperature.""" - if (self._config.get(CONF_SEND_IF_OFF) or + if (self._config[CONF_SEND_IF_OFF] or self._current_operation != STATE_OFF): mqtt.async_publish( self.hass, self._topic[CONF_FAN_MODE_COMMAND_TOPIC], - fan_mode, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + fan_mode, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: self._current_fan_mode = fan_mode @@ -591,19 +587,19 @@ async def async_set_fan_mode(self, fan_mode): async def async_set_operation_mode(self, operation_mode) -> None: """Set new operation mode.""" - qos = self._config.get(CONF_QOS) - retain = self._config.get(CONF_RETAIN) + qos = self._config[CONF_QOS] + retain = self._config[CONF_RETAIN] if self._topic[CONF_POWER_COMMAND_TOPIC] is not None: if (self._current_operation == STATE_OFF and operation_mode != STATE_OFF): mqtt.async_publish( self.hass, self._topic[CONF_POWER_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_ON), qos, retain) + self._config[CONF_PAYLOAD_ON], qos, retain) elif (self._current_operation != STATE_OFF and operation_mode == STATE_OFF): mqtt.async_publish( self.hass, self._topic[CONF_POWER_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_OFF), qos, retain) + self._config[CONF_PAYLOAD_OFF], qos, retain) if self._topic[CONF_MODE_COMMAND_TOPIC] is not None: mqtt.async_publish( @@ -622,16 +618,16 @@ def current_swing_mode(self): @property def swing_list(self): """List of available swing modes.""" - return self._config.get(CONF_SWING_MODE_LIST) + return self._config[CONF_SWING_MODE_LIST] async def async_turn_away_mode_on(self): """Turn away mode on.""" if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None: mqtt.async_publish(self.hass, self._topic[CONF_AWAY_MODE_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_ON), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_ON], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None: self._away = True @@ -642,9 +638,9 @@ async def async_turn_away_mode_off(self): if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None: mqtt.async_publish(self.hass, self._topic[CONF_AWAY_MODE_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_OFF), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_OFF], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None: self._away = False @@ -655,8 +651,8 @@ async def async_set_hold_mode(self, hold_mode): if self._topic[CONF_HOLD_COMMAND_TOPIC] is not None: mqtt.async_publish(self.hass, self._topic[CONF_HOLD_COMMAND_TOPIC], - hold_mode, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + hold_mode, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_HOLD_STATE_TOPIC] is None: self._hold = hold_mode @@ -666,9 +662,9 @@ async def async_turn_aux_heat_on(self): """Turn auxiliary heater on.""" if self._topic[CONF_AUX_COMMAND_TOPIC] is not None: mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_ON), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_ON], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_AUX_STATE_TOPIC] is None: self._aux = True @@ -678,9 +674,9 @@ async def async_turn_aux_heat_off(self): """Turn auxiliary heater off.""" if self._topic[CONF_AUX_COMMAND_TOPIC] is not None: mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC], - self._config.get(CONF_PAYLOAD_OFF), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_OFF], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._topic[CONF_AUX_STATE_TOPIC] is None: self._aux = False @@ -724,9 +720,9 @@ def supported_features(self): @property def min_temp(self): """Return the minimum temperature.""" - return self._config.get(CONF_MIN_TEMP) + return self._config[CONF_TEMP_MIN] @property def max_temp(self): """Return the maximum temperature.""" - return self._config.get(CONF_MAX_TEMP) + return self._config[CONF_TEMP_MAX] From bb5c18f7be2e525ca38c0ed8c3b39bcf8ec58c83 Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 16:07:15 +0200 Subject: [PATCH 470/605] Use relative imports in yeelight (#22839) --- homeassistant/components/yeelight/binary_sensor.py | 2 +- homeassistant/components/yeelight/light.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/yeelight/binary_sensor.py b/homeassistant/components/yeelight/binary_sensor.py index cf7bbc5244ecc0..d39af08f768d27 100644 --- a/homeassistant/components/yeelight/binary_sensor.py +++ b/homeassistant/components/yeelight/binary_sensor.py @@ -4,7 +4,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.components.yeelight import DATA_YEELIGHT, DATA_UPDATED +from . import DATA_YEELIGHT, DATA_UPDATED DEPENDENCIES = ['yeelight'] diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 912a4f99c92d15..4840ad7381a3d7 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -15,7 +15,7 @@ SUPPORT_COLOR, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP, SUPPORT_FLASH, SUPPORT_EFFECT, Light) import homeassistant.util.color as color_util -from homeassistant.components.yeelight import ( +from . import ( CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED, YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS, From a91e79ee77394ccf127cc6c65533dc66a4e3fef3 Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 16:07:34 +0200 Subject: [PATCH 471/605] Improve yeelight imports (#22804) --- homeassistant/components/yeelight/__init__.py | 20 +++++++------------ homeassistant/components/yeelight/light.py | 17 ++++++++-------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 99382bb1da9858..7a14a4d1842c5e 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -185,8 +185,8 @@ def __init__(self, hass, ipaddr, config): @property def bulb(self): """Return bulb device.""" - import yeelight if self._bulb_device is None: + import yeelight try: self._bulb_device = yeelight.Bulb(self._ipaddr, model=self._model) @@ -241,33 +241,27 @@ def is_ambilight_supported(self) -> bool: def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn on device.""" - import yeelight - - if not light_type: - light_type = yeelight.enums.LightType.Main + from yeelight import BulbException try: self.bulb.turn_on(duration=duration, light_type=light_type) - except yeelight.BulbException as ex: + except BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn off device.""" - import yeelight - - if not light_type: - light_type = yeelight.enums.LightType.Main + from yeelight import BulbException try: self.bulb.turn_off(duration=duration, light_type=light_type) - except yeelight.BulbException as ex: + except BulbException as ex: _LOGGER.error("Unable to turn the bulb off: %s", ex) return def update(self): """Read new properties from the device.""" - import yeelight + from yeelight import BulbException if not self.bulb: return @@ -275,7 +269,7 @@ def update(self): try: self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES) self._available = True - except yeelight.BulbException as ex: + except BulbException as ex: if self._available: # just inform once _LOGGER.error("Unable to update bulb status: %s", ex) self._available = False diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 4840ad7381a3d7..74796a524b0f87 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -189,6 +189,8 @@ class YeelightLight(Light): def __init__(self, device, custom_effects=None): """Initialize the Yeelight light.""" + from yeelight.enums import LightType + self.config = device.config self._device = device @@ -202,6 +204,8 @@ def __init__(self, device, custom_effects=None): self._min_mireds = None self._max_mireds = None + self._light_type = LightType.Main + if custom_effects: self._custom_effects = custom_effects else: @@ -281,8 +285,7 @@ def custom_effects_names(self): @property def light_type(self): """Return light type.""" - import yeelight - return yeelight.enums.LightType.Main + return self._light_type def _get_hs_from_properties(self): rgb = self._get_property('rgb') @@ -589,21 +592,19 @@ class YeelightAmbientLight(YeelightLight): def __init__(self, *args, **kwargs): """Initialize the Yeelight Ambient light.""" + from yeelight.enums import LightType + super().__init__(*args, **kwargs) self._min_mireds = kelvin_to_mired(6500) self._max_mireds = kelvin_to_mired(1700) + self._light_type = LightType.Ambient + @property def name(self) -> str: """Return the name of the device if any.""" return "{} ambilight".format(self.device.name) - @property - def light_type(self): - """Return light type.""" - import yeelight - return yeelight.enums.LightType.Ambient - @property def _is_nightlight_enabled(self): return False From f62d1d8d09158eb65c8afb56d9fab93b52ed1351 Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 16:07:50 +0200 Subject: [PATCH 472/605] Optimize yeelight signal handling (#22806) --- homeassistant/components/yeelight/__init__.py | 4 ++-- homeassistant/components/yeelight/binary_sensor.py | 9 +++++---- homeassistant/components/yeelight/light.py | 9 +++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 7a14a4d1842c5e..dc79e9357ffb69 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -22,7 +22,7 @@ DOMAIN = "yeelight" DATA_YEELIGHT = DOMAIN -DATA_UPDATED = '{}_data_updated'.format(DOMAIN) +DATA_UPDATED = 'yeelight_{}_data_updated' DEFAULT_NAME = 'Yeelight' DEFAULT_TRANSITION = 350 @@ -274,4 +274,4 @@ def update(self): _LOGGER.error("Unable to update bulb status: %s", ex) self._available = False - dispatcher_send(self._hass, DATA_UPDATED, self._ipaddr) + dispatcher_send(self._hass, DATA_UPDATED.format(self._ipaddr)) diff --git a/homeassistant/components/yeelight/binary_sensor.py b/homeassistant/components/yeelight/binary_sensor.py index d39af08f768d27..0b44966f15c439 100644 --- a/homeassistant/components/yeelight/binary_sensor.py +++ b/homeassistant/components/yeelight/binary_sensor.py @@ -31,14 +31,15 @@ def __init__(self, device): self._device = device @callback - def _schedule_immediate_update(self, ipaddr): - if ipaddr == self._device.ipaddr: - self.async_schedule_update_ha_state() + def _schedule_immediate_update(self): + self.async_schedule_update_ha_state() async def async_added_to_hass(self): """Handle entity which will be added.""" async_dispatcher_connect( - self.hass, DATA_UPDATED, self._schedule_immediate_update + self.hass, + DATA_UPDATED.format(self._device.ipaddr), + self._schedule_immediate_update ) @property diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 74796a524b0f87..8aa5c3d7300c26 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -212,14 +212,15 @@ def __init__(self, device, custom_effects=None): self._custom_effects = {} @callback - def _schedule_immediate_update(self, ipaddr): - if ipaddr == self.device.ipaddr: - self.async_schedule_update_ha_state(True) + def _schedule_immediate_update(self): + self.async_schedule_update_ha_state(True) async def async_added_to_hass(self): """Handle entity which will be added.""" async_dispatcher_connect( - self.hass, DATA_UPDATED, self._schedule_immediate_update + self.hass, + DATA_UPDATED.format(self._device.ipaddr), + self._schedule_immediate_update ) @property From 439197ea3e811d38ef65afe0afb4ea8303834ce7 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:08:04 +0200 Subject: [PATCH 473/605] Use dict[key] for required config keys and keys with default values. (#22828) --- .../components/mqtt/alarm_control_panel.py | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index d30c91bb9b2163..03a2ac8e3887fa 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -40,22 +40,22 @@ DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, - vol.Required(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_CODE): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string, - vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, - vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, - vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, vol.Optional(CONF_CODE_DISARM_REQUIRED, default=True): cv.boolean, vol.Optional(CONF_COMMAND_TEMPLATE, default=DEFAULT_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, + vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, + vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string, + vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, + vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, + vol.Required(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -130,9 +130,8 @@ async def _subscribe_topics(self): value_template = self._config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = self.hass - command_template = self._config.get(CONF_COMMAND_TEMPLATE) - if command_template is not None: - command_template.hass = self.hass + command_template = self._config[CONF_COMMAND_TEMPLATE] + command_template.hass = self.hass @callback def message_received(msg): @@ -154,9 +153,9 @@ def message_received(msg): self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, - {'state_topic': {'topic': self._config.get(CONF_STATE_TOPIC), + {'state_topic': {'topic': self._config[CONF_STATE_TOPIC], 'msg_callback': message_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) async def async_will_remove_from_hass(self): """Unsubscribe when removed.""" @@ -173,7 +172,7 @@ def should_poll(self): @property def name(self): """Return the name of the device.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -200,10 +199,10 @@ async def async_alarm_disarm(self, code=None): This method is a coroutine. """ - code_required = self._config.get(CONF_CODE_DISARM_REQUIRED) + code_required = self._config[CONF_CODE_DISARM_REQUIRED] if code_required and not self._validate_code(code, 'disarming'): return - payload = self._config.get(CONF_PAYLOAD_DISARM) + payload = self._config[CONF_PAYLOAD_DISARM] self._publish(code, payload) async def async_alarm_arm_home(self, code=None): @@ -211,10 +210,10 @@ async def async_alarm_arm_home(self, code=None): This method is a coroutine. """ - code_required = self._config.get(CONF_CODE_ARM_REQUIRED) + code_required = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, 'arming home'): return - action = self._config.get(CONF_PAYLOAD_ARM_HOME) + action = self._config[CONF_PAYLOAD_ARM_HOME] self._publish(code, action) async def async_alarm_arm_away(self, code=None): @@ -222,10 +221,10 @@ async def async_alarm_arm_away(self, code=None): This method is a coroutine. """ - code_required = self._config.get(CONF_CODE_ARM_REQUIRED) + code_required = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, 'arming away'): return - action = self._config.get(CONF_PAYLOAD_ARM_AWAY) + action = self._config[CONF_PAYLOAD_ARM_AWAY] self._publish(code, action) async def async_alarm_arm_night(self, code=None): @@ -233,22 +232,22 @@ async def async_alarm_arm_night(self, code=None): This method is a coroutine. """ - code_required = self._config.get(CONF_CODE_ARM_REQUIRED) + code_required = self._config[CONF_CODE_ARM_REQUIRED] if code_required and not self._validate_code(code, 'arming night'): return - action = self._config.get(CONF_PAYLOAD_ARM_NIGHT) + action = self._config[CONF_PAYLOAD_ARM_NIGHT] self._publish(code, action) def _publish(self, code, action): """Publish via mqtt.""" - command_template = self._config.get(CONF_COMMAND_TEMPLATE) + command_template = self._config[CONF_COMMAND_TEMPLATE] values = {'action': action, 'code': code} payload = command_template.async_render(**values) mqtt.async_publish( - self.hass, self._config.get(CONF_COMMAND_TOPIC), + self.hass, self._config[CONF_COMMAND_TOPIC], payload, - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], + self._config[CONF_RETAIN]) def _validate_code(self, code, state): """Validate given code.""" From a4e7708450ea8128128f2368627d03840f7369ae Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:08:47 +0200 Subject: [PATCH 474/605] Use dict[key] for required config keys and keys with default values. (#22833) --- homeassistant/components/mqtt/fan.py | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 7dff81160e0f83..d86390ee31de17 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -50,29 +50,29 @@ OSCILLATION = 'oscillation' PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_SPEED_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_SPEED_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SPEED_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_OSCILLATION_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_OSCILLATION_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_OSCILLATION_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_OSCILLATION_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_PAYLOAD_HIGH_SPEED, default=SPEED_HIGH): cv.string, + vol.Optional(CONF_PAYLOAD_LOW_SPEED, default=SPEED_LOW): cv.string, + vol.Optional(CONF_PAYLOAD_MEDIUM_SPEED, default=SPEED_MEDIUM): cv.string, vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, - vol.Optional(CONF_PAYLOAD_OSCILLATION_ON, - default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, vol.Optional(CONF_PAYLOAD_OSCILLATION_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, - vol.Optional(CONF_PAYLOAD_LOW_SPEED, default=SPEED_LOW): cv.string, - vol.Optional(CONF_PAYLOAD_MEDIUM_SPEED, default=SPEED_MEDIUM): cv.string, - vol.Optional(CONF_PAYLOAD_HIGH_SPEED, default=SPEED_HIGH): cv.string, + vol.Optional(CONF_PAYLOAD_OSCILLATION_ON, + default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_SPEED_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_SPEED_LIST, default=[SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, + vol.Optional(CONF_SPEED_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_SPEED_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -174,15 +174,15 @@ def _setup_from_config(self, config): OSCILLATION: config.get(CONF_OSCILLATION_VALUE_TEMPLATE) } self._payload = { - STATE_ON: config.get(CONF_PAYLOAD_ON), - STATE_OFF: config.get(CONF_PAYLOAD_OFF), - OSCILLATE_ON_PAYLOAD: config.get(CONF_PAYLOAD_OSCILLATION_ON), - OSCILLATE_OFF_PAYLOAD: config.get(CONF_PAYLOAD_OSCILLATION_OFF), - SPEED_LOW: config.get(CONF_PAYLOAD_LOW_SPEED), - SPEED_MEDIUM: config.get(CONF_PAYLOAD_MEDIUM_SPEED), - SPEED_HIGH: config.get(CONF_PAYLOAD_HIGH_SPEED), + STATE_ON: config[CONF_PAYLOAD_ON], + STATE_OFF: config[CONF_PAYLOAD_OFF], + OSCILLATE_ON_PAYLOAD: config[CONF_PAYLOAD_OSCILLATION_ON], + OSCILLATE_OFF_PAYLOAD: config[CONF_PAYLOAD_OSCILLATION_OFF], + SPEED_LOW: config[CONF_PAYLOAD_LOW_SPEED], + SPEED_MEDIUM: config[CONF_PAYLOAD_MEDIUM_SPEED], + SPEED_HIGH: config[CONF_PAYLOAD_HIGH_SPEED], } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None self._optimistic_oscillation = ( optimistic or self._topic[CONF_OSCILLATION_STATE_TOPIC] is None) @@ -220,7 +220,7 @@ def state_received(msg): topics[CONF_STATE_TOPIC] = { 'topic': self._topic[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} @callback def speed_received(msg): @@ -238,7 +238,7 @@ def speed_received(msg): topics[CONF_SPEED_STATE_TOPIC] = { 'topic': self._topic[CONF_SPEED_STATE_TOPIC], 'msg_callback': speed_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._speed = SPEED_OFF @callback @@ -255,7 +255,7 @@ def oscillation_received(msg): topics[CONF_OSCILLATION_STATE_TOPIC] = { 'topic': self._topic[CONF_OSCILLATION_STATE_TOPIC], 'msg_callback': oscillation_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._oscillation = False self._sub_state = await subscription.async_subscribe_topics( @@ -287,12 +287,12 @@ def is_on(self): @property def name(self) -> str: """Get entity name.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def speed_list(self) -> list: """Get the list of available speeds.""" - return self._config.get(CONF_SPEED_LIST) + return self._config[CONF_SPEED_LIST] @property def supported_features(self) -> int: @@ -316,8 +316,8 @@ async def async_turn_on(self, speed: str = None, **kwargs) -> None: """ mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload[STATE_ON], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload[STATE_ON], self._config[CONF_QOS], + self._config[CONF_RETAIN]) if speed: await self.async_set_speed(speed) @@ -328,8 +328,8 @@ async def async_turn_off(self, **kwargs) -> None: """ mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload[STATE_OFF], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload[STATE_OFF], self._config[CONF_QOS], + self._config[CONF_RETAIN]) async def async_set_speed(self, speed: str) -> None: """Set the speed of the fan. @@ -350,8 +350,8 @@ async def async_set_speed(self, speed: str) -> None: mqtt.async_publish( self.hass, self._topic[CONF_SPEED_COMMAND_TOPIC], - mqtt_payload, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + mqtt_payload, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_speed: self._speed = speed @@ -372,7 +372,7 @@ async def async_oscillate(self, oscillating: bool) -> None: mqtt.async_publish( self.hass, self._topic[CONF_OSCILLATION_COMMAND_TOPIC], - payload, self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + payload, self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic_oscillation: self._oscillation = oscillating From b1213b7a2dd1b353f1d168506de4d4b5f3d3ebd1 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:09:43 +0200 Subject: [PATCH 475/605] Use dict[key] for required config keys and keys with default values. (#22836) --- homeassistant/components/mqtt/sensor.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 1e024fdc768f4e..b6419ea2c24b36 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -35,17 +35,15 @@ DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, - vol.Optional(CONF_ICON): cv.icon, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, - vol.Optional(CONF_JSON_ATTRS, default=[]): cv.ensure_list_csv, vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, - # Integrations should never expose unique_id through configuration. - # This is an exception because MQTT is a message transport, not a protocol. + vol.Optional(CONF_ICON): cv.icon, + vol.Optional(CONF_JSON_ATTRS, default=[]): cv.ensure_list_csv, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -146,7 +144,7 @@ def message_received(msg): self._expiration_trigger = async_track_point_in_utc_time( self.hass, self.value_is_expired, expiration_at) - json_attributes = set(self._config.get(CONF_JSON_ATTRS)) + json_attributes = set(self._config[CONF_JSON_ATTRS]) if json_attributes: self._attributes = {} try: @@ -169,9 +167,9 @@ def message_received(msg): self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, - {'state_topic': {'topic': self._config.get(CONF_STATE_TOPIC), + {'state_topic': {'topic': self._config[CONF_STATE_TOPIC], 'msg_callback': message_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) async def async_will_remove_from_hass(self): """Unsubscribe when removed.""" @@ -195,7 +193,7 @@ def should_poll(self): @property def name(self): """Return the name of the sensor.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unit_of_measurement(self): @@ -205,7 +203,7 @@ def unit_of_measurement(self): @property def force_update(self): """Force update.""" - return self._config.get(CONF_FORCE_UPDATE) + return self._config[CONF_FORCE_UPDATE] @property def state(self): From 2a629069653845c167543518b7197f3fd162935e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:10:13 +0200 Subject: [PATCH 476/605] Use dict[key] for required config keys and keys with default values. (#22837) --- homeassistant/components/mqtt/switch.py | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index acfcf3de01c430..20d28b6496ca8b 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -32,15 +32,15 @@ CONF_STATE_OFF = "state_off" PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_ICON): cv.icon, - vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, - vol.Optional(CONF_STATE_ON): cv.string, + vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, vol.Optional(CONF_STATE_OFF): cv.string, + vol.Optional(CONF_STATE_ON): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -123,13 +123,13 @@ def _setup_from_config(self, config): self._config = config state_on = config.get(CONF_STATE_ON) - self._state_on = state_on if state_on else config.get(CONF_PAYLOAD_ON) + self._state_on = state_on if state_on else config[CONF_PAYLOAD_ON] state_off = config.get(CONF_STATE_OFF) self._state_off = state_off if state_off else \ - config.get(CONF_PAYLOAD_OFF) + config[CONF_PAYLOAD_OFF] - self._optimistic = config.get(CONF_OPTIMISTIC) + self._optimistic = config[CONF_OPTIMISTIC] async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -160,7 +160,7 @@ def state_message_received(msg): {CONF_STATE_TOPIC: {'topic': self._config.get(CONF_STATE_TOPIC), 'msg_callback': state_message_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) if self._optimistic: last_state = await self.async_get_last_state() @@ -182,7 +182,7 @@ def should_poll(self): @property def name(self): """Return the name of the switch.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def is_on(self): @@ -211,10 +211,10 @@ async def async_turn_on(self, **kwargs): """ mqtt.async_publish( self.hass, - self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_ON), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_COMMAND_TOPIC], + self._config[CONF_PAYLOAD_ON], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that switch has changed state. self._state = True @@ -227,10 +227,10 @@ async def async_turn_off(self, **kwargs): """ mqtt.async_publish( self.hass, - self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_OFF), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_COMMAND_TOPIC], + self._config[CONF_PAYLOAD_OFF], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that switch has changed state. self._state = False From dbb42e5890ff78f4bfae1c072a66987a25dc1d18 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:10:57 +0200 Subject: [PATCH 477/605] Use dict[key] for required config keys and keys with default values. (#22838) --- homeassistant/components/mqtt/vacuum.py | 69 ++++++++++++------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index 63a764e874663f..23a5e34b3caff5 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -102,45 +102,44 @@ def strings_to_services(strings): DEFAULT_PAYLOAD_START_PAUSE = 'start_pause' PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS): - vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]), - vol.Optional(mqtt.CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, - vol.Optional(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_PAYLOAD_TURN_ON, - default=DEFAULT_PAYLOAD_TURN_ON): cv.string, - vol.Optional(CONF_PAYLOAD_TURN_OFF, - default=DEFAULT_PAYLOAD_TURN_OFF): cv.string, - vol.Optional(CONF_PAYLOAD_RETURN_TO_BASE, - default=DEFAULT_PAYLOAD_RETURN_TO_BASE): cv.string, - vol.Optional(CONF_PAYLOAD_STOP, - default=DEFAULT_PAYLOAD_STOP): cv.string, - vol.Optional(CONF_PAYLOAD_CLEAN_SPOT, - default=DEFAULT_PAYLOAD_CLEAN_SPOT): cv.string, - vol.Optional(CONF_PAYLOAD_LOCATE, - default=DEFAULT_PAYLOAD_LOCATE): cv.string, - vol.Optional(CONF_PAYLOAD_START_PAUSE, - default=DEFAULT_PAYLOAD_START_PAUSE): cv.string, - vol.Optional(CONF_BATTERY_LEVEL_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_BATTERY_LEVEL_TEMPLATE): cv.template, - vol.Optional(CONF_CHARGING_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_BATTERY_LEVEL_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_CHARGING_TEMPLATE): cv.template, - vol.Optional(CONF_CLEANING_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_CHARGING_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_CLEANING_TEMPLATE): cv.template, - vol.Optional(CONF_DOCKED_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_CLEANING_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_DOCKED_TEMPLATE): cv.template, - vol.Optional(CONF_ERROR_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_DOCKED_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_ERROR_TEMPLATE): cv.template, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_FAN_SPEED_TEMPLATE): cv.template, - vol.Optional(CONF_SET_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_ERROR_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_FAN_SPEED_TEMPLATE): cv.template, + vol.Optional(CONF_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PAYLOAD_CLEAN_SPOT, + default=DEFAULT_PAYLOAD_CLEAN_SPOT): cv.string, + vol.Optional(CONF_PAYLOAD_LOCATE, + default=DEFAULT_PAYLOAD_LOCATE): cv.string, + vol.Optional(CONF_PAYLOAD_RETURN_TO_BASE, + default=DEFAULT_PAYLOAD_RETURN_TO_BASE): cv.string, + vol.Optional(CONF_PAYLOAD_START_PAUSE, + default=DEFAULT_PAYLOAD_START_PAUSE): cv.string, + vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string, + vol.Optional(CONF_PAYLOAD_TURN_OFF, + default=DEFAULT_PAYLOAD_TURN_OFF): cv.string, + vol.Optional(CONF_PAYLOAD_TURN_ON, + default=DEFAULT_PAYLOAD_TURN_ON): cv.string, vol.Optional(CONF_SEND_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SET_FAN_SPEED_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_STATE_TEMPLATE): cv.template, + vol.Optional(CONF_STATE_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_SUPPORTED_FEATURES, default=DEFAULT_SERVICE_STRINGS): + vol.All(cv.ensure_list, [vol.In(STRING_TO_SERVICE.keys())]), vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(mqtt.CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -206,14 +205,14 @@ def __init__(self, config, config_entry, discovery_info): MqttEntityDeviceInfo.__init__(self, device_config, config_entry) def _setup_from_config(self, config): - self._name = config.get(CONF_NAME) - supported_feature_strings = config.get(CONF_SUPPORTED_FEATURES) + self._name = config[CONF_NAME] + supported_feature_strings = config[CONF_SUPPORTED_FEATURES] self._supported_features = strings_to_services( supported_feature_strings ) - self._fan_speed_list = config.get(CONF_FAN_SPEED_LIST) - self._qos = config.get(mqtt.CONF_QOS) - self._retain = config.get(mqtt.CONF_RETAIN) + self._fan_speed_list = config[CONF_FAN_SPEED_LIST] + self._qos = config[mqtt.CONF_QOS] + self._retain = config[mqtt.CONF_RETAIN] self._command_topic = config.get(mqtt.CONF_COMMAND_TOPIC) self._set_fan_speed_topic = config.get(CONF_SET_FAN_SPEED_TOPIC) From 58220a94484b5531b24002ee4a68d33af87d3378 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:11:20 +0200 Subject: [PATCH 478/605] Use dict[key] for required config keys and keys with default values. (#22829) --- .../components/mqtt/binary_sensor.py | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index f942091984a24b..95daad9b262868 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -32,17 +32,15 @@ DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, - vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OFF_DELAY): vol.All(vol.Coerce(int), vol.Range(min=0)), - # Integrations should never expose unique_id through configuration. - # This is an exception because MQTT is a message transport, not a protocol + vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, + vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema) @@ -135,15 +133,15 @@ def state_message_received(msg): if value_template is not None: payload = value_template.async_render_with_possible_json_value( payload, variables={'entity_id': self.entity_id}) - if payload == self._config.get(CONF_PAYLOAD_ON): + if payload == self._config[CONF_PAYLOAD_ON]: self._state = True - elif payload == self._config.get(CONF_PAYLOAD_OFF): + elif payload == self._config[CONF_PAYLOAD_OFF]: self._state = False else: # Payload is not for this entity _LOGGER.warning('No matching payload found' ' for entity: %s with state_topic: %s', - self._config.get(CONF_NAME), - self._config.get(CONF_STATE_TOPIC)) + self._config[CONF_NAME], + self._config[CONF_STATE_TOPIC]) return if self._delay_listener is not None: @@ -159,9 +157,9 @@ def state_message_received(msg): self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, - {'state_topic': {'topic': self._config.get(CONF_STATE_TOPIC), + {'state_topic': {'topic': self._config[CONF_STATE_TOPIC], 'msg_callback': state_message_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) async def async_will_remove_from_hass(self): """Unsubscribe when removed.""" @@ -178,7 +176,7 @@ def should_poll(self): @property def name(self): """Return the name of the binary sensor.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def is_on(self): @@ -193,7 +191,7 @@ def device_class(self): @property def force_update(self): """Force update.""" - return self._config.get(CONF_FORCE_UPDATE) + return self._config[CONF_FORCE_UPDATE] @property def unique_id(self): From fa2e07d7c5ba5543e2c8ab7d48b91dcba4127957 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sun, 7 Apr 2019 16:11:45 +0200 Subject: [PATCH 479/605] Use dict[key] for required config keys and keys with default values. (#22830) --- homeassistant/components/mqtt/camera.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 34e83a51f28eeb..f651050b6c8596 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -26,9 +26,9 @@ DEPENDENCIES = ['mqtt'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string }) @@ -103,7 +103,7 @@ def message_received(msg): self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, - {'state_topic': {'topic': self._config.get(CONF_TOPIC), + {'state_topic': {'topic': self._config[CONF_TOPIC], 'msg_callback': message_received, 'qos': self._qos, 'encoding': None}}) @@ -121,7 +121,7 @@ def async_camera_image(self): @property def name(self): """Return the name of this camera.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): From 02b7fd93ed2684cca2f5cf31229a99dcca317e1d Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 07:37:27 -0700 Subject: [PATCH 480/605] Fix for rate limits should be optional (#22823) --- homeassistant/components/mobile_app/const.py | 5 ++++ homeassistant/components/mobile_app/notify.py | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 52ca0aa39930c6..31364ba063da55 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -42,6 +42,11 @@ ATTR_OS_VERSION = 'os_version' ATTR_PUSH_TOKEN = 'push_token' ATTR_PUSH_URL = 'push_url' +ATTR_PUSH_RATE_LIMITS = 'rateLimits' +ATTR_PUSH_RATE_LIMITS_ERRORS = 'errors' +ATTR_PUSH_RATE_LIMITS_MAXIMUM = 'maximum' +ATTR_PUSH_RATE_LIMITS_RESETS_AT = 'resetsAt' +ATTR_PUSH_RATE_LIMITS_SUCCESSFUL = 'successful' ATTR_SUPPORTS_ENCRYPTION = 'supports_encryption' ATTR_EVENT_DATA = 'event_data' diff --git a/homeassistant/components/mobile_app/notify.py b/homeassistant/components/mobile_app/notify.py index 0120b1a6ffbac2..8d2ac1b97ecef4 100644 --- a/homeassistant/components/mobile_app/notify.py +++ b/homeassistant/components/mobile_app/notify.py @@ -8,13 +8,18 @@ from homeassistant.components.notify import ( ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService) -from homeassistant.components.mobile_app.const import ( - ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, ATTR_DEVICE_NAME, - ATTR_OS_VERSION, ATTR_PUSH_TOKEN, ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, - DOMAIN) + from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.util.dt as dt_util +from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, + ATTR_DEVICE_NAME, ATTR_OS_VERSION, ATTR_PUSH_RATE_LIMITS, + ATTR_PUSH_RATE_LIMITS_ERRORS, + ATTR_PUSH_RATE_LIMITS_MAXIMUM, + ATTR_PUSH_RATE_LIMITS_RESETS_AT, + ATTR_PUSH_RATE_LIMITS_SUCCESSFUL, ATTR_PUSH_TOKEN, + ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, DOMAIN) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mobile_app'] @@ -38,16 +43,21 @@ def push_registrations(hass): # pylint: disable=invalid-name def log_rate_limits(hass, device_name, resp, level=logging.INFO): """Output rate limit log line at given level.""" - rate_limits = resp['rateLimits'] - resetsAt = dt_util.parse_datetime(rate_limits['resetsAt']) - resetsAtTime = resetsAt - datetime.now(timezone.utc) + if ATTR_PUSH_RATE_LIMITS not in resp: + return + + rate_limits = resp[ATTR_PUSH_RATE_LIMITS] + resetsAt = rate_limits[ATTR_PUSH_RATE_LIMITS_RESETS_AT] + resetsAtTime = (dt_util.parse_datetime(resetsAt) - + datetime.now(timezone.utc)) rate_limit_msg = ("mobile_app push notification rate limits for %s: " "%d sent, %d allowed, %d errors, " "resets in %s") _LOGGER.log(level, rate_limit_msg, device_name, - rate_limits['successful'], - rate_limits['maximum'], rate_limits['errors'], + rate_limits[ATTR_PUSH_RATE_LIMITS_SUCCESSFUL], + rate_limits[ATTR_PUSH_RATE_LIMITS_MAXIMUM], + rate_limits[ATTR_PUSH_RATE_LIMITS_ERRORS], str(resetsAtTime).split(".")[0]) From c7a49e0820a5f1c7b4eae1e49c7a219fda4267c4 Mon Sep 17 00:00:00 2001 From: roblandry Date: Sun, 7 Apr 2019 13:07:05 -0400 Subject: [PATCH 481/605] Fix glances docker container errors (#22846) * Fix unavailable container errors * Update to dev * Use const --- homeassistant/components/glances/sensor.py | 41 ++++++++++++++-------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/glances/sensor.py b/homeassistant/components/glances/sensor.py index e59b9144b4c28f..db8f0397887e89 100644 --- a/homeassistant/components/glances/sensor.py +++ b/homeassistant/components/glances/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PORT, CONF_USERNAME, CONF_PASSWORD, CONF_SSL, - CONF_VERIFY_SSL, CONF_RESOURCES, TEMP_CELSIUS) + CONF_VERIFY_SSL, CONF_RESOURCES, STATE_UNAVAILABLE, TEMP_CELSIUS) from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -183,21 +183,34 @@ async def async_update(self): self._state = sensor['value'] elif self.type == 'docker_active': count = 0 - for container in value['docker']['containers']: - if container['Status'] == 'running' or \ - 'Up' in container['Status']: - count += 1 - self._state = count + try: + for container in value['docker']['containers']: + if container['Status'] == 'running' or \ + 'Up' in container['Status']: + count += 1 + self._state = count + except KeyError: + self._state = count elif self.type == 'docker_cpu_use': - use = 0.0 - for container in value['docker']['containers']: - use += container['cpu']['total'] - self._state = round(use, 1) + cpu_use = 0.0 + try: + for container in value['docker']['containers']: + if container['Status'] == 'running' or \ + 'Up' in container['Status']: + cpu_use += container['cpu']['total'] + self._state = round(cpu_use, 1) + except KeyError: + self._state = STATE_UNAVAILABLE elif self.type == 'docker_memory_use': - use = 0.0 - for container in value['docker']['containers']: - use += container['memory']['usage'] - self._state = round(use / 1024**2, 1) + mem_use = 0.0 + try: + for container in value['docker']['containers']: + if container['Status'] == 'running' or \ + 'Up' in container['Status']: + mem_use += container['memory']['usage'] + self._state = round(mem_use / 1024**2, 1) + except KeyError: + self._state = STATE_UNAVAILABLE class GlancesData: From 3fde1d3bab11dff8e46520c2797d978a973cac0c Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 7 Apr 2019 13:08:08 -0400 Subject: [PATCH 482/605] coerce duration and lookback to int so they can be used in template automation (#22819) --- homeassistant/components/camera/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 10739a1c7bb55c..2ddab537acc09e 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -83,8 +83,8 @@ CAMERA_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({ vol.Required(CONF_FILENAME): cv.template, - vol.Optional(CONF_DURATION, default=30): int, - vol.Optional(CONF_LOOKBACK, default=0): int, + vol.Optional(CONF_DURATION, default=30): vol.Coerce(int), + vol.Optional(CONF_LOOKBACK, default=0): vol.Coerce(int), }) WS_TYPE_CAMERA_THUMBNAIL = 'camera_thumbnail' From abe85c73ae73e309742bdec10cc3fd3845446a5e Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 7 Apr 2019 12:42:16 -0700 Subject: [PATCH 483/605] Fix flaky test (#22850) --- tests/components/stream/test_hls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/stream/test_hls.py b/tests/components/stream/test_hls.py index a2c962ffb4552e..9d898d96d78546 100644 --- a/tests/components/stream/test_hls.py +++ b/tests/components/stream/test_hls.py @@ -110,7 +110,7 @@ async def test_stream_ended(hass): while await track.recv() is not None: segments += 1 - assert segments == 3 + assert segments > 1 assert not track.get_segment() # Stop stream, if it hasn't quit already From e407226afc13122a1f846345b6ecc27fb814a36d Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 22:05:38 +0200 Subject: [PATCH 484/605] Fix yeelight possible array change during iteration (#22849) --- homeassistant/components/yeelight/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index dc79e9357ffb69..9b9778fd5d28be 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -134,7 +134,7 @@ def device_discovered(service, info): discovery.listen(hass, SERVICE_YEELIGHT, device_discovered) def update(event): - for device in yeelight_data.values(): + for device in list(yeelight_data.values()): device.update() track_time_interval( From a40a0c40426e96549560b122eefdb3782578a27e Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 7 Apr 2019 14:21:29 -0600 Subject: [PATCH 485/605] Bump aioambient to 0.3.0 (#22855) * Bump aioambient to 0.3.0 * Updated requirements * Removed old REQUIREMENTS reference --- homeassistant/components/ambient_station/__init__.py | 2 -- homeassistant/components/ambient_station/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index cb0a2067d9f170..a715ded1bb7174 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -20,8 +20,6 @@ ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE, TYPE_BINARY_SENSOR, TYPE_SENSOR) -REQUIREMENTS = ['aioambient==0.2.0'] - _LOGGER = logging.getLogger(__name__) DATA_CONFIG = 'config' diff --git a/homeassistant/components/ambient_station/manifest.json b/homeassistant/components/ambient_station/manifest.json index 13a74fec26e2e2..11d2ad3668e33f 100644 --- a/homeassistant/components/ambient_station/manifest.json +++ b/homeassistant/components/ambient_station/manifest.json @@ -3,7 +3,7 @@ "name": "Ambient station", "documentation": "https://www.home-assistant.io/components/ambient_station", "requirements": [ - "aioambient==0.1.3" + "aioambient==0.3.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 265ff9de860b8b..926549160a2f4c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -97,7 +97,7 @@ abodepy==0.15.0 afsapi==0.0.4 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.3.0 # homeassistant.components.asuswrt aioasuswrt==1.1.21 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 54b6230ae8115e..c9890f92626591 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -36,7 +36,7 @@ PyTransportNSW==0.1.1 YesssSMS==0.2.3 # homeassistant.components.ambient_station -aioambient==0.1.3 +aioambient==0.3.0 # homeassistant.components.automatic aioautomatic==0.6.5 From 3086e1d39d32e98957485cebf9ec8d21251fd93b Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Sun, 7 Apr 2019 22:03:38 -0400 Subject: [PATCH 486/605] get temp and color for light during init and poll (#22847) --- .../components/zha/core/channels/lighting.py | 4 ++++ homeassistant/components/zha/light.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index 696a15b483be8a..c7cbdc67db9789 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -33,6 +33,10 @@ async def async_configure(self): async def async_initialize(self, from_cache): """Initialize channel.""" await self.fetch_color_capabilities(True) + await self.get_attribute_value( + 'color_temperature', from_cache=from_cache) + await self.get_attribute_value('current_x', from_cache=from_cache) + await self.get_attribute_value('current_y', from_cache=from_cache) async def fetch_color_capabilities(self, from_cache): """Get the color configuration.""" diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index cebc18e6a3eb84..eadc9e03af0321 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -253,6 +253,20 @@ async def async_get_state(self, from_cache=True): if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( 'current_level', from_cache=from_cache) + if self._color_channel: + color_capabilities = self._color_channel.get_color_capabilities() + if color_capabilities is not None and\ + color_capabilities & CAPABILITIES_COLOR_TEMP: + self._color_temp = await\ + self._color_channel.get_attribute_value( + 'color_temperature', from_cache=from_cache) + if color_capabilities is not None and\ + color_capabilities & CAPABILITIES_COLOR_XY: + color_x = await self._color_channel.get_attribute_value( + 'current_x', from_cache=from_cache) + color_y = await self._color_channel.get_attribute_value( + 'current_y', from_cache=from_cache) + self._hs_color = color_util.color_xy_to_hs(color_x, color_y) async def refresh(self, time): """Call async_get_state at an interval.""" From 8bebd8583f78ce3a829b7f7517b9181f7ef35e5c Mon Sep 17 00:00:00 2001 From: cdce8p <30130371+cdce8p@users.noreply.github.com> Date: Mon, 8 Apr 2019 06:01:05 +0200 Subject: [PATCH 487/605] Fix manifest codeowners (#22871) * Added individual files section * Replaced some manifest/codeowners --- CODEOWNERS | 8 ++++++-- homeassistant/components/cover/manifest.json | 2 +- homeassistant/components/demo/manifest.json | 2 +- script/manifest/codeowners.py | 8 ++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index a5c06f991c9a2a..6ce7388e0d1610 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -45,13 +45,13 @@ homeassistant/components/configurator/* @home-assistant/core homeassistant/components/conversation/* @home-assistant/core homeassistant/components/coolmaster/* @OnFreund homeassistant/components/counter/* @fabaff -homeassistant/components/cover/* @cdce8p +homeassistant/components/cover/* @home-assistant/core homeassistant/components/cpuspeed/* @fabaff homeassistant/components/cups/* @fabaff homeassistant/components/daikin/* @fredrike @rofrantz homeassistant/components/darksky/* @fabaff homeassistant/components/deconz/* @kane610 -homeassistant/components/demo/* @fabaff +homeassistant/components/demo/* @home-assistant/core homeassistant/components/digital_ocean/* @fabaff homeassistant/components/discogs/* @thibmaek homeassistant/components/doorbird/* @oblogic7 @@ -243,3 +243,7 @@ homeassistant/components/zha/* @dmulcahey @adminiuga homeassistant/components/zone/* @home-assistant/core homeassistant/components/zoneminder/* @rohankapoorcom homeassistant/components/zwave/* @home-assistant/z-wave + +# Individual files +homeassistant/components/group/cover @cdce8p +homeassistant/components/demo/weather @fabaff diff --git a/homeassistant/components/cover/manifest.json b/homeassistant/components/cover/manifest.json index f39f7fb0650645..da5a644334cb5e 100644 --- a/homeassistant/components/cover/manifest.json +++ b/homeassistant/components/cover/manifest.json @@ -7,6 +7,6 @@ "group" ], "codeowners": [ - "@cdce8p" + "@home-assistant/core" ] } diff --git a/homeassistant/components/demo/manifest.json b/homeassistant/components/demo/manifest.json index 08cf75a3c5399d..859cac597dc276 100644 --- a/homeassistant/components/demo/manifest.json +++ b/homeassistant/components/demo/manifest.json @@ -9,6 +9,6 @@ "zone" ], "codeowners": [ - "@fabaff" + "@home-assistant/core" ] } diff --git a/script/manifest/codeowners.py b/script/manifest/codeowners.py index 9745f3b82f2f4a..96b2b252e3d4e1 100755 --- a/script/manifest/codeowners.py +++ b/script/manifest/codeowners.py @@ -27,6 +27,12 @@ # Integrations """ +INDIVIDUAL_FILES = """ +# Individual files +homeassistant/components/group/cover @cdce8p +homeassistant/components/demo/weather @fabaff +""" + def generate(): """Generate CODEOWNERS.""" @@ -39,6 +45,8 @@ def generate(): parts.append("homeassistant/components/{}/* {}".format( manifest['domain'], ' '.join(manifest['codeowners']))) + parts.append('\n' + INDIVIDUAL_FILES.strip()) + return '\n'.join(parts) From 4982c0b196890ca69167d2a4ef51af3a08739d35 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 7 Apr 2019 22:02:03 -0600 Subject: [PATCH 488/605] Added REQUIREMENTS back to Ambient (#22875) --- homeassistant/components/ambient_station/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index a715ded1bb7174..6dee4637a96ae5 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -20,6 +20,8 @@ ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE, TYPE_BINARY_SENSOR, TYPE_SENSOR) +REQUIREMENTS = ['aioambient==0.3.0'] + _LOGGER = logging.getLogger(__name__) DATA_CONFIG = 'config' From 2d287d2abe2ac4c465383f1a33a2a381652473db Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 8 Apr 2019 09:22:55 +0200 Subject: [PATCH 489/605] Fix content_type handling ingress (#22864) --- homeassistant/components/hassio/ingress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 91224c6f54da77..4f7c99618c19d4 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -118,6 +118,7 @@ async def _handle_request( return web.Response( headers=headers, status=result.status, + content_type=result.content_type, body=body ) @@ -145,8 +146,7 @@ def _init_header( # filter flags for name, value in request.headers.items(): - if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE, - hdrs.CONTENT_ENCODING): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_ENCODING): continue headers[name] = value From d8119b2281d742c6120c195b5e04b390b76d997b Mon Sep 17 00:00:00 2001 From: Wolfgang Malgadey Date: Fri, 5 Apr 2019 02:52:06 +0200 Subject: [PATCH 490/605] Fix tado turn on off (#22291) * fix for turn on and off, with new pyTado missing blank line * removed, because can't push * uploaded the file through github again --- homeassistant/components/tado/__init__.py | 5 +++++ homeassistant/components/tado/climate.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 6808729685eb11..8d3f541972e50c 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -121,3 +121,8 @@ def set_zone_overlay(self, zone_id, mode, temperature=None, duration=None): """Wrap for setZoneOverlay(..).""" self.tado.setZoneOverlay(zone_id, mode, temperature, duration) self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg + + def set_zone_off(self, zone_id, mode): + """Set a zone to off.""" + self.tado.setZoneOverlay(zone_id, mode, None, None, 'HEATING', 'OFF') + self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 56c670184b5b44..90d5f076974f77 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -363,7 +363,7 @@ def _control_heating(self): if self._current_operation == CONST_MODE_OFF: _LOGGER.info("Switching mytado.com to OFF for zone %s", self.zone_name) - self._store.set_zone_overlay(self.zone_id, CONST_OVERLAY_MANUAL) + self._store.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL) self._overlay_mode = self._current_operation return From ed9d1e776fd078a313a20f1f66658728f27ad2cf Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 31 Mar 2019 21:30:45 -0700 Subject: [PATCH 491/605] Add new mobile_app webhook command: get_zones (#22604) ## Description: Adds a new `mobile_app` webhook command, `get_zones`, which just returns all zones. ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. --- homeassistant/components/mobile_app/const.py | 5 +-- .../components/mobile_app/helpers.py | 5 +-- .../components/mobile_app/webhook.py | 32 +++++++++++++------ homeassistant/components/zone/config_flow.py | 2 +- tests/components/mobile_app/test_webhook.py | 26 +++++++++++++++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 61c50e97c6e838..b59c631ba9936d 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,6 +67,7 @@ WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_GET_ZONES = 'get_zones' WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' WEBHOOK_TYPE_UPDATE_LOCATION = 'update_location' @@ -74,8 +75,8 @@ WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES] diff --git a/homeassistant/components/mobile_app/helpers.py b/homeassistant/components/mobile_app/helpers.py index 60bd8b4e1d6bec..ee593588ef8ff6 100644 --- a/homeassistant/components/mobile_app/helpers.py +++ b/homeassistant/components/mobile_app/helpers.py @@ -6,6 +6,7 @@ from aiohttp.web import json_response, Response from homeassistant.core import Context +from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.typing import HomeAssistantType from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_NAME, @@ -133,9 +134,9 @@ def savable_state(hass: HomeAssistantType) -> Dict: def webhook_response(data, *, registration: Dict, status: int = 200, headers: Dict = None) -> Response: """Return a encrypted response if registration supports it.""" - data = json.dumps(data) + data = json.dumps(data, cls=JSONEncoder) - if CONF_SECRET in registration: + if registration[ATTR_SUPPORTS_ENCRYPTION]: keylen, encrypt = setup_encrypt() key = registration[CONF_SECRET].encode("utf-8") diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index aafa6046d110a5..71c6d0d66734e0 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -9,6 +9,8 @@ DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) +from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN + from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, CONF_WEBHOOK_ID, HTTP_BAD_REQUEST, HTTP_CREATED) @@ -33,9 +35,10 @@ DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -87,16 +90,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, enc_data = req_data[ATTR_WEBHOOK_ENCRYPTED_DATA] webhook_payload = _decrypt_payload(registration[CONF_SECRET], enc_data) - if webhook_type not in WEBHOOK_SCHEMAS: + if webhook_type not in WEBHOOK_TYPES: _LOGGER.error('Received invalid webhook type: %s', webhook_type) return empty_okay_response() - try: - data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) - except vol.Invalid as ex: - err = vol.humanize.humanize_error(webhook_payload, ex) - _LOGGER.error('Received invalid webhook payload: %s', err) - return empty_okay_response(headers=headers) + data = webhook_payload + + if webhook_type in WEBHOOK_SCHEMAS: + try: + data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) + except vol.Invalid as ex: + err = vol.humanize.humanize_error(webhook_payload, ex) + _LOGGER.error('Received invalid webhook payload: %s', err) + return empty_okay_response(headers=headers) context = registration_context(registration) @@ -261,3 +267,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, return webhook_response(resp, registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_GET_ZONES: + zones = (hass.states.get(entity_id) for entity_id + in sorted(hass.states.async_entity_ids(ZONE_DOMAIN))) + return webhook_response(list(zones), registration=registration, + headers=headers) diff --git a/homeassistant/components/zone/config_flow.py b/homeassistant/components/zone/config_flow.py index bf221a828adc33..a7b968676d6d8b 100644 --- a/homeassistant/components/zone/config_flow.py +++ b/homeassistant/components/zone/config_flow.py @@ -14,7 +14,7 @@ @callback def configured_zones(hass): - """Return a set of the configured hosts.""" + """Return a set of the configured zones.""" return set((slugify(entry.data[CONF_NAME])) for entry in hass.config_entries.async_entries(DOMAIN)) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index a70e8ba1275290..ad19a70a806ac2 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -4,8 +4,10 @@ import pytest from homeassistant.components.mobile_app.const import CONF_SECRET +from homeassistant.components.zone import DOMAIN as ZONE_DOMAIN from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.core import callback +from homeassistant.setup import async_setup_component from tests.common import async_mock_service @@ -100,6 +102,30 @@ async def test_webhook_update_registration(webhook_client, hass_client): # noqa assert CONF_SECRET not in update_json +async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 + """Test that we can get zones properly.""" + await async_setup_component(hass, ZONE_DOMAIN, { + ZONE_DOMAIN: { + 'name': 'test', + 'latitude': 32.880837, + 'longitude': -117.237561, + 'radius': 250, + } + }) + + resp = await webhook_client.post( + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), + json={'type': 'get_zones'} + ) + + assert resp.status == 200 + + json = await resp.json() + assert len(json) == 1 + assert json[0]['entity_id'] == 'zone.home' + + async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 create_registrations, # noqa: F401, F811, E501 caplog): # noqa: E501 F811 From 7a8aa79f191ed633babc1134238017c164b306f3 Mon Sep 17 00:00:00 2001 From: Chris Helming Date: Fri, 5 Apr 2019 23:02:38 -0400 Subject: [PATCH 492/605] Add optional rtsp_port for Foscam (#22786) * add optional rtsp port for config * getting rid of default=None * removing vol.Any --- homeassistant/components/foscam/camera.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 1b4ee039053935..8adac6586252df 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -19,6 +19,7 @@ REQUIREMENTS = ['libpyfoscam==1.0'] CONF_IP = 'ip' +CONF_RTSP_PORT = 'rtsp_port' DEFAULT_NAME = 'Foscam Camera' DEFAULT_PORT = 88 @@ -31,6 +32,7 @@ vol.Required(CONF_USERNAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_RTSP_PORT): cv.port }) @@ -58,11 +60,12 @@ def __init__(self, device_info): self._foscam_session = FoscamCamera( ip_address, port, self._username, self._password, verbose=False) - self._rtsp_port = None - result, response = self._foscam_session.get_port_info() - if result == 0: - self._rtsp_port = response.get('rtspPort') or \ - response.get('mediaPort') + self._rtsp_port = device_info.get(CONF_RTSP_PORT) + if not self._rtsp_port: + result, response = self._foscam_session.get_port_info() + if result == 0: + self._rtsp_port = response.get('rtspPort') or \ + response.get('mediaPort') def camera_image(self): """Return a still image response from the camera.""" From abb531c06bf2dba25f49cdd4992b34202233cf5c Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 16:07:34 +0200 Subject: [PATCH 493/605] Improve yeelight imports (#22804) --- homeassistant/components/yeelight/__init__.py | 20 +++++++------------ homeassistant/components/yeelight/light.py | 17 ++++++++-------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 99382bb1da9858..7a14a4d1842c5e 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -185,8 +185,8 @@ def __init__(self, hass, ipaddr, config): @property def bulb(self): """Return bulb device.""" - import yeelight if self._bulb_device is None: + import yeelight try: self._bulb_device = yeelight.Bulb(self._ipaddr, model=self._model) @@ -241,33 +241,27 @@ def is_ambilight_supported(self) -> bool: def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn on device.""" - import yeelight - - if not light_type: - light_type = yeelight.enums.LightType.Main + from yeelight import BulbException try: self.bulb.turn_on(duration=duration, light_type=light_type) - except yeelight.BulbException as ex: + except BulbException as ex: _LOGGER.error("Unable to turn the bulb on: %s", ex) return def turn_off(self, duration=DEFAULT_TRANSITION, light_type=None): """Turn off device.""" - import yeelight - - if not light_type: - light_type = yeelight.enums.LightType.Main + from yeelight import BulbException try: self.bulb.turn_off(duration=duration, light_type=light_type) - except yeelight.BulbException as ex: + except BulbException as ex: _LOGGER.error("Unable to turn the bulb off: %s", ex) return def update(self): """Read new properties from the device.""" - import yeelight + from yeelight import BulbException if not self.bulb: return @@ -275,7 +269,7 @@ def update(self): try: self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES) self._available = True - except yeelight.BulbException as ex: + except BulbException as ex: if self._available: # just inform once _LOGGER.error("Unable to update bulb status: %s", ex) self._available = False diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 912a4f99c92d15..e842d91bf2abf6 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -189,6 +189,8 @@ class YeelightLight(Light): def __init__(self, device, custom_effects=None): """Initialize the Yeelight light.""" + from yeelight.enums import LightType + self.config = device.config self._device = device @@ -202,6 +204,8 @@ def __init__(self, device, custom_effects=None): self._min_mireds = None self._max_mireds = None + self._light_type = LightType.Main + if custom_effects: self._custom_effects = custom_effects else: @@ -281,8 +285,7 @@ def custom_effects_names(self): @property def light_type(self): """Return light type.""" - import yeelight - return yeelight.enums.LightType.Main + return self._light_type def _get_hs_from_properties(self): rgb = self._get_property('rgb') @@ -589,21 +592,19 @@ class YeelightAmbientLight(YeelightLight): def __init__(self, *args, **kwargs): """Initialize the Yeelight Ambient light.""" + from yeelight.enums import LightType + super().__init__(*args, **kwargs) self._min_mireds = kelvin_to_mired(6500) self._max_mireds = kelvin_to_mired(1700) + self._light_type = LightType.Ambient + @property def name(self) -> str: """Return the name of the device if any.""" return "{} ambilight".format(self.device.name) - @property - def light_type(self): - """Return light type.""" - import yeelight - return yeelight.enums.LightType.Ambient - @property def _is_nightlight_enabled(self): return False From 82f6bed3a3f24cf3928ddd4574db7d28aa53f908 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 01:16:54 -0700 Subject: [PATCH 494/605] Add a new mobile_app webhook command to get config (#22813) * Add a new mobile_app webhook command to get config * Limit fields returned --- homeassistant/components/mobile_app/const.py | 6 ++-- .../components/mobile_app/webhook.py | 24 ++++++++++--- tests/components/mobile_app/test_webhook.py | 34 +++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index b59c631ba9936d..52ca0aa39930c6 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,6 +67,7 @@ WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_FIRE_EVENT = 'fire_event' +WEBHOOK_TYPE_GET_CONFIG = 'get_config' WEBHOOK_TYPE_GET_ZONES = 'get_zones' WEBHOOK_TYPE_REGISTER_SENSOR = 'register_sensor' WEBHOOK_TYPE_RENDER_TEMPLATE = 'render_template' @@ -75,8 +76,9 @@ WEBHOOK_TYPE_UPDATE_SENSOR_STATES = 'update_sensor_states' WEBHOOK_TYPES = [WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, - WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_GET_CONFIG, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES] diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 71c6d0d66734e0..a57d3930f50544 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -8,7 +8,7 @@ ATTR_DEV_ID, DOMAIN as DT_DOMAIN, SERVICE_SEE as DT_SEE) - +from homeassistant.components.frontend import MANIFEST_JSON from homeassistant.components.zone.const import DOMAIN as ZONE_DOMAIN from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA, @@ -36,9 +36,9 @@ ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_ZONES, - WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, - WEBHOOK_TYPE_UPDATE_LOCATION, + WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_CONFIG, + WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, + WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -273,3 +273,19 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, in sorted(hass.states.async_entity_ids(ZONE_DOMAIN))) return webhook_response(list(zones), registration=registration, headers=headers) + + if webhook_type == WEBHOOK_TYPE_GET_CONFIG: + + hass_config = hass.config.as_dict() + + return webhook_response({ + 'latitude': hass_config['latitude'], + 'longitude': hass_config['longitude'], + 'elevation': hass_config['elevation'], + 'unit_system': hass_config['unit_system'], + 'location_name': hass_config['location_name'], + 'time_zone': hass_config['time_zone'], + 'components': hass_config['components'], + 'version': hass_config['version'], + 'theme_color': MANIFEST_JSON['theme_color'], + }, registration=registration, headers=headers) diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index ad19a70a806ac2..43eac28ec18421 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -126,6 +126,40 @@ async def test_webhook_handle_get_zones(hass, create_registrations, # noqa: F40 assert json[0]['entity_id'] == 'zone.home' +async def test_webhook_handle_get_config(hass, create_registrations, # noqa: F401, F811, E501 + webhook_client): # noqa: F811 + """Test that we can get config properly.""" + resp = await webhook_client.post( + '/api/webhook/{}'.format(create_registrations[1]['webhook_id']), + json={'type': 'get_config'} + ) + + assert resp.status == 200 + + json = await resp.json() + if 'components' in json: + json['components'] = set(json['components']) + if 'whitelist_external_dirs' in json: + json['whitelist_external_dirs'] = \ + set(json['whitelist_external_dirs']) + + hass_config = hass.config.as_dict() + + expected_dict = { + 'latitude': hass_config['latitude'], + 'longitude': hass_config['longitude'], + 'elevation': hass_config['elevation'], + 'unit_system': hass_config['unit_system'], + 'location_name': hass_config['location_name'], + 'time_zone': hass_config['time_zone'], + 'components': hass_config['components'], + 'version': hass_config['version'], + 'theme_color': '#03A9F4', # Default frontend theme color + } + + assert expected_dict == json + + async def test_webhook_returns_error_incorrect_json(webhook_client, # noqa: F401, F811, E501 create_registrations, # noqa: F401, F811, E501 caplog): # noqa: E501 F811 From 474fc21c66105a6b6ae5ac61639c04be9e372279 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 01:17:14 -0700 Subject: [PATCH 495/605] Fix for optional values in the update_location webhook call (#22817) * Fix for optional values in the update_location webhook call * Square brackets instead of .get --- .../components/mobile_app/webhook.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index a57d3930f50544..7a83ba4e978f34 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -145,18 +145,26 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION: see_payload = { ATTR_DEV_ID: registration[ATTR_DEVICE_ID], - ATTR_LOCATION_NAME: data.get(ATTR_LOCATION_NAME), - ATTR_GPS: data.get(ATTR_GPS), - ATTR_GPS_ACCURACY: data.get(ATTR_GPS_ACCURACY), - ATTR_BATTERY: data.get(ATTR_BATTERY), - ATTR_ATTRIBUTES: { - ATTR_SPEED: data.get(ATTR_SPEED), - ATTR_ALTITUDE: data.get(ATTR_ALTITUDE), - ATTR_COURSE: data.get(ATTR_COURSE), - ATTR_VERTICAL_ACCURACY: data.get(ATTR_VERTICAL_ACCURACY), - } + ATTR_GPS: data[ATTR_GPS], + ATTR_GPS_ACCURACY: data[ATTR_GPS_ACCURACY], } + for key in (ATTR_LOCATION_NAME, ATTR_BATTERY): + value = data.get(key) + if value is not None: + see_payload[key] = value + + attrs = {} + + for key in (ATTR_ALTITUDE, ATTR_COURSE, + ATTR_SPEED, ATTR_VERTICAL_ACCURACY): + value = data.get(key) + if value is not None: + attrs[key] = value + + if attrs: + see_payload[ATTR_ATTRIBUTES] = attrs + try: await hass.services.async_call(DT_DOMAIN, DT_SEE, see_payload, From f51e8c3012e30490ed3210278eb6f5a309c6e787 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 7 Apr 2019 13:08:08 -0400 Subject: [PATCH 496/605] coerce duration and lookback to int so they can be used in template automation (#22819) --- homeassistant/components/camera/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index e453cdfd1a166a..9ec081166bb310 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -88,8 +88,8 @@ CAMERA_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({ vol.Required(CONF_FILENAME): cv.template, - vol.Optional(CONF_DURATION, default=30): int, - vol.Optional(CONF_LOOKBACK, default=0): int, + vol.Optional(CONF_DURATION, default=30): vol.Coerce(int), + vol.Optional(CONF_LOOKBACK, default=0): vol.Coerce(int), }) WS_TYPE_CAMERA_THUMBNAIL = 'camera_thumbnail' From 236e484dc27a88712c88f30e04511d028924b554 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Sun, 7 Apr 2019 07:37:27 -0700 Subject: [PATCH 497/605] Fix for rate limits should be optional (#22823) --- homeassistant/components/mobile_app/const.py | 5 ++++ homeassistant/components/mobile_app/notify.py | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 52ca0aa39930c6..31364ba063da55 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -42,6 +42,11 @@ ATTR_OS_VERSION = 'os_version' ATTR_PUSH_TOKEN = 'push_token' ATTR_PUSH_URL = 'push_url' +ATTR_PUSH_RATE_LIMITS = 'rateLimits' +ATTR_PUSH_RATE_LIMITS_ERRORS = 'errors' +ATTR_PUSH_RATE_LIMITS_MAXIMUM = 'maximum' +ATTR_PUSH_RATE_LIMITS_RESETS_AT = 'resetsAt' +ATTR_PUSH_RATE_LIMITS_SUCCESSFUL = 'successful' ATTR_SUPPORTS_ENCRYPTION = 'supports_encryption' ATTR_EVENT_DATA = 'event_data' diff --git a/homeassistant/components/mobile_app/notify.py b/homeassistant/components/mobile_app/notify.py index 0120b1a6ffbac2..8d2ac1b97ecef4 100644 --- a/homeassistant/components/mobile_app/notify.py +++ b/homeassistant/components/mobile_app/notify.py @@ -8,13 +8,18 @@ from homeassistant.components.notify import ( ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService) -from homeassistant.components.mobile_app.const import ( - ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, ATTR_DEVICE_NAME, - ATTR_OS_VERSION, ATTR_PUSH_TOKEN, ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, - DOMAIN) + from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.util.dt as dt_util +from .const import (ATTR_APP_DATA, ATTR_APP_ID, ATTR_APP_VERSION, + ATTR_DEVICE_NAME, ATTR_OS_VERSION, ATTR_PUSH_RATE_LIMITS, + ATTR_PUSH_RATE_LIMITS_ERRORS, + ATTR_PUSH_RATE_LIMITS_MAXIMUM, + ATTR_PUSH_RATE_LIMITS_RESETS_AT, + ATTR_PUSH_RATE_LIMITS_SUCCESSFUL, ATTR_PUSH_TOKEN, + ATTR_PUSH_URL, DATA_CONFIG_ENTRIES, DOMAIN) + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['mobile_app'] @@ -38,16 +43,21 @@ def push_registrations(hass): # pylint: disable=invalid-name def log_rate_limits(hass, device_name, resp, level=logging.INFO): """Output rate limit log line at given level.""" - rate_limits = resp['rateLimits'] - resetsAt = dt_util.parse_datetime(rate_limits['resetsAt']) - resetsAtTime = resetsAt - datetime.now(timezone.utc) + if ATTR_PUSH_RATE_LIMITS not in resp: + return + + rate_limits = resp[ATTR_PUSH_RATE_LIMITS] + resetsAt = rate_limits[ATTR_PUSH_RATE_LIMITS_RESETS_AT] + resetsAtTime = (dt_util.parse_datetime(resetsAt) - + datetime.now(timezone.utc)) rate_limit_msg = ("mobile_app push notification rate limits for %s: " "%d sent, %d allowed, %d errors, " "resets in %s") _LOGGER.log(level, rate_limit_msg, device_name, - rate_limits['successful'], - rate_limits['maximum'], rate_limits['errors'], + rate_limits[ATTR_PUSH_RATE_LIMITS_SUCCESSFUL], + rate_limits[ATTR_PUSH_RATE_LIMITS_MAXIMUM], + rate_limits[ATTR_PUSH_RATE_LIMITS_ERRORS], str(resetsAtTime).split(".")[0]) From c5d4b7c24336777ac289579f99c5f19ea269ad0f Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 16:07:15 +0200 Subject: [PATCH 498/605] Use relative imports in yeelight (#22839) --- homeassistant/components/yeelight/binary_sensor.py | 2 +- homeassistant/components/yeelight/light.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/yeelight/binary_sensor.py b/homeassistant/components/yeelight/binary_sensor.py index cf7bbc5244ecc0..d39af08f768d27 100644 --- a/homeassistant/components/yeelight/binary_sensor.py +++ b/homeassistant/components/yeelight/binary_sensor.py @@ -4,7 +4,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.components.yeelight import DATA_YEELIGHT, DATA_UPDATED +from . import DATA_YEELIGHT, DATA_UPDATED DEPENDENCIES = ['yeelight'] diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index e842d91bf2abf6..74796a524b0f87 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -15,7 +15,7 @@ SUPPORT_COLOR, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP, SUPPORT_FLASH, SUPPORT_EFFECT, Light) import homeassistant.util.color as color_util -from homeassistant.components.yeelight import ( +from . import ( CONF_TRANSITION, DATA_YEELIGHT, CONF_MODE_MUSIC, CONF_SAVE_ON_CHANGE, CONF_CUSTOM_EFFECTS, DATA_UPDATED, YEELIGHT_SERVICE_SCHEMA, DOMAIN, ATTR_TRANSITIONS, From 3f15b6b2d3ff6f02d3950001ca81925ed081b6ee Mon Sep 17 00:00:00 2001 From: zewelor Date: Sun, 7 Apr 2019 22:05:38 +0200 Subject: [PATCH 499/605] Fix yeelight possible array change during iteration (#22849) --- homeassistant/components/yeelight/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 7a14a4d1842c5e..0cb9d41fe4b12f 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -134,7 +134,7 @@ def device_discovered(service, info): discovery.listen(hass, SERVICE_YEELIGHT, device_discovered) def update(event): - for device in yeelight_data.values(): + for device in list(yeelight_data.values()): device.update() track_time_interval( From 3a79e37cde39d861789b37dcb6451e9765251bc2 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 8 Apr 2019 09:22:55 +0200 Subject: [PATCH 500/605] Fix content_type handling ingress (#22864) --- homeassistant/components/hassio/ingress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 91224c6f54da77..4f7c99618c19d4 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -118,6 +118,7 @@ async def _handle_request( return web.Response( headers=headers, status=result.status, + content_type=result.content_type, body=body ) @@ -145,8 +146,7 @@ def _init_header( # filter flags for name, value in request.headers.items(): - if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_TYPE, - hdrs.CONTENT_ENCODING): + if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_ENCODING): continue headers[name] = value From 3f739739700eea212b9747a72f46d6e7de5a2ee7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 8 Apr 2019 08:00:22 +0000 Subject: [PATCH 501/605] Bumped version to 0.91.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 6ece276307e5a8..28bdf2f7fc3807 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 137d80452dd9262870dfcf440551b092fe1b817c Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Mon, 8 Apr 2019 01:13:26 -0700 Subject: [PATCH 502/605] Minor sensor fixes (#22884) * Minor sensor fixes * Fix tests --- homeassistant/components/mobile_app/const.py | 2 +- homeassistant/components/mobile_app/sensor.py | 2 +- homeassistant/components/mobile_app/webhook.py | 4 ++-- tests/components/mobile_app/test_entity.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 31364ba063da55..05d240da909db8 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -171,7 +171,7 @@ vol.Required(ATTR_SENSOR_NAME): cv.string, vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, - vol.Required(ATTR_SENSOR_UOM): cv.string, + vol.Optional(ATTR_SENSOR_UOM): cv.string, vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), vol.Optional(ATTR_SENSOR_ICON, default='mdi:cellphone'): cv.icon, }) diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index c6a53ce57ec7c9..b2846a6002b17f 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -55,4 +55,4 @@ def state(self): @property def unit_of_measurement(self): """Return the unit of measurement this sensor expresses itself in.""" - return self._config[ATTR_SENSOR_UOM] + return self._config.get(ATTR_SENSOR_UOM) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 7a83ba4e978f34..28ef6bccd6add2 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -228,7 +228,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, data[ATTR_SENSOR_TYPE]) async_dispatcher_send(hass, register_signal, data) - return webhook_response({"status": "registered"}, + return webhook_response({'success': True}, registration=registration, status=HTTP_CREATED, headers=headers) @@ -271,7 +271,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, new_state) - resp[unique_id] = {"status": "okay"} + resp[unique_id] = {'success': True} return webhook_response(resp, registration=registration, headers=headers) diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index 5dc285cfe9ed45..3d8e575f686753 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -35,7 +35,7 @@ async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401 assert reg_resp.status == 201 json = await reg_resp.json() - assert json == {'status': 'registered'} + assert json == {'success': True} entity = hass.states.get('sensor.battery_state') assert entity is not None @@ -122,7 +122,7 @@ async def test_sensor_id_no_dupes(hass, create_registrations, # noqa: F401, F81 assert reg_resp.status == 201 reg_json = await reg_resp.json() - assert reg_json == {'status': 'registered'} + assert reg_json == {'success': True} dupe_resp = await webhook_client.post(webhook_url, json=payload) From a04d44d97a59760ebb81517f25db9081d8ba4421 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Mon, 8 Apr 2019 01:13:26 -0700 Subject: [PATCH 503/605] Minor sensor fixes (#22884) * Minor sensor fixes * Fix tests --- homeassistant/components/mobile_app/const.py | 2 +- homeassistant/components/mobile_app/sensor.py | 2 +- homeassistant/components/mobile_app/webhook.py | 4 ++-- tests/components/mobile_app/test_entity.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 31364ba063da55..05d240da909db8 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -171,7 +171,7 @@ vol.Required(ATTR_SENSOR_NAME): cv.string, vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES), vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string, - vol.Required(ATTR_SENSOR_UOM): cv.string, + vol.Optional(ATTR_SENSOR_UOM): cv.string, vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float), vol.Optional(ATTR_SENSOR_ICON, default='mdi:cellphone'): cv.icon, }) diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index c6a53ce57ec7c9..b2846a6002b17f 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -55,4 +55,4 @@ def state(self): @property def unit_of_measurement(self): """Return the unit of measurement this sensor expresses itself in.""" - return self._config[ATTR_SENSOR_UOM] + return self._config.get(ATTR_SENSOR_UOM) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 7a83ba4e978f34..28ef6bccd6add2 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -228,7 +228,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, data[ATTR_SENSOR_TYPE]) async_dispatcher_send(hass, register_signal, data) - return webhook_response({"status": "registered"}, + return webhook_response({'success': True}, registration=registration, status=HTTP_CREATED, headers=headers) @@ -271,7 +271,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, new_state) - resp[unique_id] = {"status": "okay"} + resp[unique_id] = {'success': True} return webhook_response(resp, registration=registration, headers=headers) diff --git a/tests/components/mobile_app/test_entity.py b/tests/components/mobile_app/test_entity.py index 5dc285cfe9ed45..3d8e575f686753 100644 --- a/tests/components/mobile_app/test_entity.py +++ b/tests/components/mobile_app/test_entity.py @@ -35,7 +35,7 @@ async def test_sensor(hass, create_registrations, webhook_client): # noqa: F401 assert reg_resp.status == 201 json = await reg_resp.json() - assert json == {'status': 'registered'} + assert json == {'success': True} entity = hass.states.get('sensor.battery_state') assert entity is not None @@ -122,7 +122,7 @@ async def test_sensor_id_no_dupes(hass, create_registrations, # noqa: F401, F81 assert reg_resp.status == 201 reg_json = await reg_resp.json() - assert reg_json == {'status': 'registered'} + assert reg_json == {'success': True} dupe_resp = await webhook_client.post(webhook_url, json=payload) From a4ffc9e37aacace178c470278e938e504f852212 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Mon, 8 Apr 2019 13:48:19 +0100 Subject: [PATCH 504/605] add myself as codeowner (#22885) --- CODEOWNERS | 7 +++++++ homeassistant/components/cisco_ios/manifest.json | 2 +- .../components/cisco_mobility_express/manifest.json | 2 +- homeassistant/components/cisco_webex_teams/manifest.json | 2 +- homeassistant/components/ciscospark/manifest.json | 2 +- homeassistant/components/enigma2/manifest.json | 2 +- homeassistant/components/hikvisioncam/manifest.json | 2 +- homeassistant/components/luci/manifest.json | 2 +- 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6ce7388e0d1610..30a91a7c76d2c3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -38,6 +38,10 @@ homeassistant/components/braviatv/* @robbiet480 homeassistant/components/broadlink/* @danielhiversen homeassistant/components/brunt/* @eavanvalkenburg homeassistant/components/bt_smarthub/* @jxwolstenholme +homeassistant/components/cisco_ios/* @fbradyirl +homeassistant/components/cisco_mobility_express/* @fbradyirl +homeassistant/components/cisco_webex_teams/* @fbradyirl +homeassistant/components/ciscospark/* @fbradyirl homeassistant/components/cloud/* @home-assistant/core homeassistant/components/cloudflare/* @ludeeus homeassistant/components/config/* @home-assistant/core @@ -61,6 +65,7 @@ homeassistant/components/edp_redy/* @abmantis homeassistant/components/egardia/* @jeroenterheerdt homeassistant/components/eight_sleep/* @mezz64 homeassistant/components/emby/* @mezz64 +homeassistant/components/enigma2/* @fbradyirl homeassistant/components/ephember/* @ttroy50 homeassistant/components/eq3btsmart/* @rytilahti homeassistant/components/esphome/* @OttoWinter @@ -86,6 +91,7 @@ homeassistant/components/harmony/* @ehendrix23 homeassistant/components/hassio/* @home-assistant/hass-io homeassistant/components/heos/* @andrewsayre homeassistant/components/hikvision/* @mezz64 +homeassistant/components/hikvisioncam/* @fbradyirl homeassistant/components/history/* @home-assistant/core homeassistant/components/history_graph/* @andrey-git homeassistant/components/hive/* @Rendili @KJonline @@ -120,6 +126,7 @@ homeassistant/components/linux_battery/* @fabaff homeassistant/components/liveboxplaytv/* @pschmitt homeassistant/components/logger/* @home-assistant/core homeassistant/components/lovelace/* @home-assistant/core +homeassistant/components/luci/* @fbradyirl homeassistant/components/luftdaten/* @fabaff homeassistant/components/mastodon/* @fabaff homeassistant/components/matrix/* @tinloaf diff --git a/homeassistant/components/cisco_ios/manifest.json b/homeassistant/components/cisco_ios/manifest.json index d1a9e9933b922e..9a12ba252e374a 100644 --- a/homeassistant/components/cisco_ios/manifest.json +++ b/homeassistant/components/cisco_ios/manifest.json @@ -6,5 +6,5 @@ "pexpect==4.6.0" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/cisco_mobility_express/manifest.json b/homeassistant/components/cisco_mobility_express/manifest.json index 6bd56ccd15e487..d1b4687c2cdeff 100644 --- a/homeassistant/components/cisco_mobility_express/manifest.json +++ b/homeassistant/components/cisco_mobility_express/manifest.json @@ -6,5 +6,5 @@ "ciscomobilityexpress==0.1.5" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/cisco_webex_teams/manifest.json b/homeassistant/components/cisco_webex_teams/manifest.json index d13b893ce69913..21c4efe071c95c 100644 --- a/homeassistant/components/cisco_webex_teams/manifest.json +++ b/homeassistant/components/cisco_webex_teams/manifest.json @@ -6,5 +6,5 @@ "webexteamssdk==1.1.1" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/ciscospark/manifest.json b/homeassistant/components/ciscospark/manifest.json index c6b0c42e89c5c6..926925a7bf19ec 100644 --- a/homeassistant/components/ciscospark/manifest.json +++ b/homeassistant/components/ciscospark/manifest.json @@ -6,5 +6,5 @@ "ciscosparkapi==0.4.2" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/enigma2/manifest.json b/homeassistant/components/enigma2/manifest.json index 78eb9f9deff82a..6619e6c1f0dac0 100644 --- a/homeassistant/components/enigma2/manifest.json +++ b/homeassistant/components/enigma2/manifest.json @@ -6,5 +6,5 @@ "openwebifpy==3.1.0" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/hikvisioncam/manifest.json b/homeassistant/components/hikvisioncam/manifest.json index ec63425572df27..f2bb0822d17c19 100644 --- a/homeassistant/components/hikvisioncam/manifest.json +++ b/homeassistant/components/hikvisioncam/manifest.json @@ -6,5 +6,5 @@ "hikvision==0.4" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } diff --git a/homeassistant/components/luci/manifest.json b/homeassistant/components/luci/manifest.json index 46e1702c36e672..13b8b172a5dce9 100644 --- a/homeassistant/components/luci/manifest.json +++ b/homeassistant/components/luci/manifest.json @@ -6,5 +6,5 @@ "openwrt-luci-rpc==1.0.5" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fbradyirl"] } From a0d6e08421c0e1a903f3a5e7befd882f3de6f402 Mon Sep 17 00:00:00 2001 From: Finbarr Brady Date: Mon, 8 Apr 2019 13:49:52 +0100 Subject: [PATCH 505/605] Bump pypi module version for enigma2 (#22886) * Bug fix for #22727 * Update requirements_all.txt * Update manifest.json --- homeassistant/components/enigma2/manifest.json | 2 +- homeassistant/components/enigma2/media_player.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/enigma2/manifest.json b/homeassistant/components/enigma2/manifest.json index 6619e6c1f0dac0..d523bd72b720dc 100644 --- a/homeassistant/components/enigma2/manifest.json +++ b/homeassistant/components/enigma2/manifest.json @@ -3,7 +3,7 @@ "name": "Enigma2", "documentation": "https://www.home-assistant.io/components/enigma2", "requirements": [ - "openwebifpy==3.1.0" + "openwebifpy==3.1.1" ], "dependencies": [], "codeowners": ["@fbradyirl"] diff --git a/homeassistant/components/enigma2/media_player.py b/homeassistant/components/enigma2/media_player.py index 0b6f995be97123..11c3e0fe3ceab4 100644 --- a/homeassistant/components/enigma2/media_player.py +++ b/homeassistant/components/enigma2/media_player.py @@ -14,7 +14,7 @@ STATE_OFF, STATE_ON, STATE_PLAYING, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['openwebifpy==3.1.0'] +REQUIREMENTS = ['openwebifpy==3.1.1'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 926549160a2f4c..ddab662c1d544b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -779,7 +779,7 @@ openhomedevice==0.4.2 opensensemap-api==0.1.5 # homeassistant.components.enigma2 -openwebifpy==3.1.0 +openwebifpy==3.1.1 # homeassistant.components.luci openwrt-luci-rpc==1.0.5 From 45a43592bd7ac664fd3509afa15f4c2ede2ad785 Mon Sep 17 00:00:00 2001 From: John Raahauge <43510812+AZDane@users.noreply.github.com> Date: Mon, 8 Apr 2019 05:53:00 -0700 Subject: [PATCH 506/605] Fix position of add_entities of binary sensor (#22866) * Bugfix - binary_sensor.py * Added features to Concord232 Alarm Panel * Added New Line End Of File * Deleted Whitespace * Back to original Removed added feature and sticking to bugfix --- homeassistant/components/concord232/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/concord232/binary_sensor.py b/homeassistant/components/concord232/binary_sensor.py index 5aff0f09983c38..c1a31eb9ead989 100644 --- a/homeassistant/components/concord232/binary_sensor.py +++ b/homeassistant/components/concord232/binary_sensor.py @@ -74,7 +74,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): ) ) - add_entities(sensors, True) + add_entities(sensors, True) def get_opening_type(zone): From 8cc5cc7f43cdc76328869ab072535268701875dd Mon Sep 17 00:00:00 2001 From: cgtobi Date: Mon, 8 Apr 2019 15:18:52 +0200 Subject: [PATCH 507/605] Add zwave network key validator (#22785) * Add zwave network key validator * Move validator to zwave component * Move validator to zwave component * Move stuff * Move stuff * Remove helper and replace with voluptuous method * Add test * Fix long line * Improve tests * Add more negative tests * Remove unnecessary assertion * Make the linter happy * Remove print --- homeassistant/components/zwave/__init__.py | 3 ++- tests/components/zwave/test_init.py | 30 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index aca36aabd3b4ac..6028e5547c6c79 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -159,7 +159,8 @@ DOMAIN: vol.Schema({ vol.Optional(CONF_AUTOHEAL, default=DEFAULT_CONF_AUTOHEAL): cv.boolean, vol.Optional(CONF_CONFIG_PATH): cv.string, - vol.Optional(CONF_NETWORK_KEY): cv.string, + vol.Optional(CONF_NETWORK_KEY): + vol.All(cv.string, vol.Match(r'(0x\w\w,\s?){15}0x\w\w')), vol.Optional(CONF_DEVICE_CONFIG, default={}): vol.Schema({cv.entity_id: DEVICE_CONFIG_SCHEMA_ENTRY}), vol.Optional(CONF_DEVICE_CONFIG_GLOB, default={}): diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 66011f3e6ee00d..3f0c082591cef8 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -3,6 +3,7 @@ from collections import OrderedDict from datetime import datetime from pytz import utc +import voluptuous as vol import unittest from unittest.mock import patch, MagicMock @@ -83,6 +84,35 @@ async def test_network_options(hass, mock_openzwave): assert network.options.config_path == 'mock_config_path' +async def test_network_key_validation(hass, mock_openzwave): + """Test network key validation.""" + test_values = [ + ('0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ' + '0x0C, 0x0D, 0x0E, 0x0F, 0x10'), + ('0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,' + '0x0E,0x0F,0x10'), + ] + for value in test_values: + result = zwave.CONFIG_SCHEMA({'zwave': {'network_key': value}}) + assert result['zwave']['network_key'] == value + + +async def test_erronous_network_key_fails_validation(hass, mock_openzwave): + """Test failing erronous network key validation.""" + test_values = [ + ('0x 01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ' + '0x0C, 0x0D, 0x0E, 0x0F, 0x10'), + ('0X01,0X02,0X03,0X04,0X05,0X06,0X07,0X08,0X09,0X0A,0X0B,0X0C,0X0D,' + '0X0E,0X0F,0X10'), + 'invalid', + '1234567', + 1234567 + ] + for value in test_values: + with pytest.raises(vol.Invalid): + zwave.CONFIG_SCHEMA({'zwave': {'network_key': value}}) + + async def test_auto_heal_midnight(hass, mock_openzwave): """Test network auto-heal at midnight.""" await async_setup_component(hass, 'zwave', { From d8c716037775c1e2c68a9fcd86640153b07f1c57 Mon Sep 17 00:00:00 2001 From: akasma74 Date: Mon, 8 Apr 2019 16:21:13 +0300 Subject: [PATCH 508/605] force_update=False (not None) (#22867) because force_update: boolean --- homeassistant/components/rflink/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/rflink/binary_sensor.py b/homeassistant/components/rflink/binary_sensor.py index e98fb756659cd7..4e487eb6e81577 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -53,7 +53,7 @@ class RflinkBinarySensor(RflinkDevice, BinarySensorDevice): """Representation of an Rflink binary sensor.""" def __init__(self, device_id, device_class=None, - force_update=None, off_delay=None, + force_update=False, off_delay=None, **kwargs): """Handle sensor specific args and super init.""" self._state = None From 36c135c785f7daa9f739103befbff68d38f86a1a Mon Sep 17 00:00:00 2001 From: Matt Snyder Date: Mon, 8 Apr 2019 08:22:31 -0500 Subject: [PATCH 509/605] Stream support for Doorbird component (#22876) * Support stream source for doorbird live camera * Support stream source for doorbird live camera * Support stream component on Doorbird camera entities * Bump library version * Update manifest * Lint * Correct parameter order --- homeassistant/components/doorbird/__init__.py | 2 +- homeassistant/components/doorbird/camera.py | 19 ++++++++++++++++--- .../components/doorbird/manifest.json | 2 +- requirements_all.txt | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/doorbird/__init__.py b/homeassistant/components/doorbird/__init__.py index d477836425d5e6..25a2c5caff91d9 100644 --- a/homeassistant/components/doorbird/__init__.py +++ b/homeassistant/components/doorbird/__init__.py @@ -11,7 +11,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import dt as dt_util, slugify -REQUIREMENTS = ['doorbirdpy==2.0.6'] +REQUIREMENTS = ['doorbirdpy==2.0.8'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/doorbird/camera.py b/homeassistant/components/doorbird/camera.py index 272a3cb932a0df..a93b0fbf1948cb 100644 --- a/homeassistant/components/doorbird/camera.py +++ b/homeassistant/components/doorbird/camera.py @@ -6,7 +6,7 @@ import aiohttp import async_timeout -from homeassistant.components.camera import Camera +from homeassistant.components.camera import Camera, SUPPORT_STREAM from homeassistant.helpers.aiohttp_client import async_get_clientsession from . import DOMAIN as DOORBIRD_DOMAIN @@ -32,7 +32,8 @@ async def async_setup_platform(hass, config, async_add_entities, DoorBirdCamera( device.live_image_url, _CAMERA_LIVE.format(doorstation.name), - _LIVE_INTERVAL), + _LIVE_INTERVAL, + device.rtsp_live_video_url), DoorBirdCamera( device.history_image_url(1, 'doorbell'), _CAMERA_LAST_VISITOR.format(doorstation.name), @@ -47,15 +48,27 @@ async def async_setup_platform(hass, config, async_add_entities, class DoorBirdCamera(Camera): """The camera on a DoorBird device.""" - def __init__(self, url, name, interval=None): + def __init__(self, url, name, interval=None, stream_url=None): """Initialize the camera on a DoorBird device.""" self._url = url + self._stream_url = stream_url self._name = name self._last_image = None + self._supported_features = SUPPORT_STREAM if self._stream_url else 0 self._interval = interval or datetime.timedelta self._last_update = datetime.datetime.min super().__init__() + @property + def stream_source(self): + """Return the stream source.""" + return self._stream_url + + @property + def supported_features(self): + """Return supported features.""" + return self._supported_features + @property def name(self): """Get the name of the camera.""" diff --git a/homeassistant/components/doorbird/manifest.json b/homeassistant/components/doorbird/manifest.json index f65af20f93c237..3fb9fdc753b7d5 100644 --- a/homeassistant/components/doorbird/manifest.json +++ b/homeassistant/components/doorbird/manifest.json @@ -3,7 +3,7 @@ "name": "Doorbird", "documentation": "https://www.home-assistant.io/components/doorbird", "requirements": [ - "doorbirdpy==2.0.6" + "doorbirdpy==2.0.8" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index ddab662c1d544b..de505dec18f55a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -344,7 +344,7 @@ distro==1.4.0 dlipower==0.7.165 # homeassistant.components.doorbird -doorbirdpy==2.0.6 +doorbirdpy==2.0.8 # homeassistant.components.dovado dovado==0.4.1 From c9ec166f4b79cd59f501b358eacf344d1f590943 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 8 Apr 2019 15:28:42 +0200 Subject: [PATCH 510/605] Add MQTT climate two-point target temperature support (#22860) * Add MQTT climate two-point target temperature support * Sort * Fix test --- homeassistant/components/mqtt/climate.py | 104 ++++++++++++++++++++++- tests/components/mqtt/test_climate.py | 69 ++++++++++++++- 2 files changed, 170 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 0f4229c8688219..17d32984bb528d 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -10,7 +10,10 @@ ATTR_OPERATION_MODE, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, STATE_AUTO, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, STATE_HEAT, SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, - SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE) + SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE, + ATTR_TARGET_TEMP_LOW, + ATTR_TARGET_TEMP_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW, + SUPPORT_TARGET_TEMPERATURE_HIGH) from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM from homeassistant.const import ( ATTR_TEMPERATURE, CONF_DEVICE, CONF_NAME, CONF_VALUE_TEMPLATE, STATE_OFF, @@ -41,6 +44,10 @@ CONF_TEMPERATURE_COMMAND_TOPIC = 'temperature_command_topic' CONF_TEMPERATURE_STATE_TOPIC = 'temperature_state_topic' CONF_TEMPERATURE_STATE_TEMPLATE = 'temperature_state_template' +CONF_TEMPERATURE_LOW_COMMAND_TOPIC = 'temperature_low_command_topic' +CONF_TEMPERATURE_LOW_STATE_TOPIC = 'temperature_low_state_topic' +CONF_TEMPERATURE_HIGH_COMMAND_TOPIC = 'temperature_high_command_topic' +CONF_TEMPERATURE_HIGH_STATE_TOPIC = 'temperature_high_state_topic' CONF_FAN_MODE_COMMAND_TOPIC = 'fan_mode_command_topic' CONF_FAN_MODE_STATE_TOPIC = 'fan_mode_state_topic' CONF_FAN_MODE_STATE_TEMPLATE = 'fan_mode_state_template' @@ -130,6 +137,12 @@ vol.Optional(CONF_TEMP_MAX, default=DEFAULT_MAX_TEMP): vol.Coerce(float), vol.Optional(CONF_TEMP_STEP, default=1.0): vol.Coerce(float), vol.Optional(CONF_TEMPERATURE_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TEMPERATURE_HIGH_COMMAND_TOPIC): + mqtt.valid_publish_topic, + vol.Optional(CONF_TEMPERATURE_HIGH_STATE_TOPIC): + mqtt.valid_subscribe_topic, + vol.Optional(CONF_TEMPERATURE_LOW_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TEMPERATURE_LOW_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_TEMPERATURE_STATE_TEMPLATE): cv.template, vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_UNIQUE_ID): cv.string, @@ -186,6 +199,8 @@ def __init__(self, hass, config, config_entry, discovery_hash): self._topic = None self._value_templates = None self._target_temperature = None + self._target_temperature_low = None + self._target_temperature_high = None self._current_fan_mode = None self._current_operation = None self._current_swing_mode = None @@ -230,6 +245,8 @@ def _setup_from_config(self, config): CONF_POWER_COMMAND_TOPIC, CONF_MODE_COMMAND_TOPIC, CONF_TEMPERATURE_COMMAND_TOPIC, + CONF_TEMPERATURE_LOW_COMMAND_TOPIC, + CONF_TEMPERATURE_HIGH_COMMAND_TOPIC, CONF_FAN_MODE_COMMAND_TOPIC, CONF_SWING_MODE_COMMAND_TOPIC, CONF_AWAY_MODE_COMMAND_TOPIC, @@ -238,6 +255,8 @@ def _setup_from_config(self, config): CONF_POWER_STATE_TOPIC, CONF_MODE_STATE_TOPIC, CONF_TEMPERATURE_STATE_TOPIC, + CONF_TEMPERATURE_LOW_STATE_TOPIC, + CONF_TEMPERATURE_HIGH_STATE_TOPIC, CONF_FAN_MODE_STATE_TOPIC, CONF_SWING_MODE_STATE_TOPIC, CONF_AWAY_MODE_STATE_TOPIC, @@ -250,8 +269,16 @@ def _setup_from_config(self, config): # set to None in non-optimistic mode self._target_temperature = self._current_fan_mode = \ self._current_operation = self._current_swing_mode = None + self._target_temperature_low = None + self._target_temperature_high = None + if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is None: self._target_temperature = config[CONF_TEMP_INITIAL] + if self._topic[CONF_TEMPERATURE_LOW_STATE_TOPIC] is None: + self._target_temperature_low = config[CONF_TEMP_INITIAL] + if self._topic[CONF_TEMPERATURE_HIGH_STATE_TOPIC] is None: + self._target_temperature_high = config[CONF_TEMP_INITIAL] + if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: self._current_fan_mode = SPEED_LOW if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: @@ -339,6 +366,38 @@ def handle_temperature_received(msg): 'msg_callback': handle_temperature_received, 'qos': qos} + @callback + def handle_temperature_low_received(msg): + """Handle target temperature low coming via MQTT.""" + try: + self._target_temperature_low = float(msg.payload) + self.async_write_ha_state() + except ValueError: + _LOGGER.error("Could not parse low temperature from %s", + msg.payload) + + if self._topic[CONF_TEMPERATURE_LOW_STATE_TOPIC] is not None: + topics[CONF_TEMPERATURE_LOW_STATE_TOPIC] = { + 'topic': self._topic[CONF_TEMPERATURE_LOW_STATE_TOPIC], + 'msg_callback': handle_temperature_low_received, + 'qos': qos} + + @callback + def handle_temperature_high_received(msg): + """Handle target temperature high coming via MQTT.""" + try: + self._target_temperature_high = float(msg.payload) + self.async_write_ha_state() + except ValueError: + _LOGGER.error("Could not parse high temperature from %s", + msg.payload) + + if self._topic[CONF_TEMPERATURE_HIGH_STATE_TOPIC] is not None: + topics[CONF_TEMPERATURE_HIGH_STATE_TOPIC] = { + 'topic': self._topic[CONF_TEMPERATURE_HIGH_STATE_TOPIC], + 'msg_callback': handle_temperature_high_received, + 'qos': qos} + @callback def handle_fan_mode_received(msg): """Handle receiving fan mode via MQTT.""" @@ -498,6 +557,16 @@ def target_temperature(self): """Return the temperature we try to reach.""" return self._target_temperature + @property + def target_temperature_low(self): + """Return the low target temperature we try to reach.""" + return self._target_temperature_low + + @property + def target_temperature_high(self): + """Return the high target temperature we try to reach.""" + return self._target_temperature_high + @property def current_operation(self): """Return current operation ie. heat, cool, idle.""" @@ -556,6 +625,31 @@ async def async_set_temperature(self, **kwargs): kwargs.get(ATTR_TEMPERATURE), self._config[CONF_QOS], self._config[CONF_RETAIN]) + if kwargs.get(ATTR_TARGET_TEMP_LOW) is not None: + if self._topic[CONF_TEMPERATURE_LOW_STATE_TOPIC] is None: + # optimistic mode + self._target_temperature_low = kwargs[ATTR_TARGET_TEMP_LOW] + + if (self._config[CONF_SEND_IF_OFF] or + self._current_operation != STATE_OFF): + mqtt.async_publish( + self.hass, self._topic[CONF_TEMPERATURE_LOW_COMMAND_TOPIC], + kwargs.get(ATTR_TARGET_TEMP_LOW), self._config[CONF_QOS], + self._config[CONF_RETAIN]) + + if kwargs.get(ATTR_TARGET_TEMP_HIGH) is not None: + if self._topic[CONF_TEMPERATURE_HIGH_STATE_TOPIC] is None: + # optimistic mode + self._target_temperature_high = kwargs[ATTR_TARGET_TEMP_HIGH] + + if (self._config[CONF_SEND_IF_OFF] or + self._current_operation != STATE_OFF): + mqtt.async_publish( + self.hass, + self._topic[CONF_TEMPERATURE_HIGH_COMMAND_TOPIC], + kwargs.get(ATTR_TARGET_TEMP_HIGH), self._config[CONF_QOS], + self._config[CONF_RETAIN]) + # Always optimistic? self.async_write_ha_state() @@ -691,6 +785,14 @@ def supported_features(self): (self._topic[CONF_TEMPERATURE_COMMAND_TOPIC] is not None): support |= SUPPORT_TARGET_TEMPERATURE + if (self._topic[CONF_TEMPERATURE_LOW_STATE_TOPIC] is not None) or \ + (self._topic[CONF_TEMPERATURE_LOW_COMMAND_TOPIC] is not None): + support |= SUPPORT_TARGET_TEMPERATURE_LOW + + if (self._topic[CONF_TEMPERATURE_HIGH_STATE_TOPIC] is not None) or \ + (self._topic[CONF_TEMPERATURE_HIGH_COMMAND_TOPIC] is not None): + support |= SUPPORT_TARGET_TEMPERATURE_HIGH + if (self._topic[CONF_MODE_COMMAND_TOPIC] is not None) or \ (self._topic[CONF_MODE_STATE_TOPIC] is not None): support |= SUPPORT_OPERATION_MODE diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index 7bdfe8f452fa69..a8e1ae6111ecbe 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -15,7 +15,8 @@ SUPPORT_AUX_HEAT, SUPPORT_AWAY_MODE, SUPPORT_FAN_MODE, SUPPORT_HOLD_MODE, SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE, STATE_AUTO, - STATE_COOL, STATE_HEAT, STATE_DRY, STATE_FAN_ONLY) + STATE_COOL, STATE_HEAT, STATE_DRY, STATE_FAN_ONLY, + SUPPORT_TARGET_TEMPERATURE_LOW, SUPPORT_TARGET_TEMPERATURE_HIGH) from homeassistant.components.mqtt.discovery import async_start from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE from homeassistant.setup import setup_component @@ -35,6 +36,8 @@ 'name': 'test', 'mode_command_topic': 'mode-topic', 'temperature_command_topic': 'temperature-topic', + 'temperature_low_command_topic': 'temperature-low-topic', + 'temperature_high_command_topic': 'temperature-high-topic', 'fan_mode_command_topic': 'fan-mode-topic', 'swing_mode_command_topic': 'swing-mode-topic', 'away_mode_command_topic': 'away-mode-topic', @@ -75,7 +78,9 @@ def test_supported_features(self): state = self.hass.states.get(ENTITY_CLIMATE) support = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE | SUPPORT_SWING_MODE | SUPPORT_FAN_MODE | SUPPORT_AWAY_MODE | - SUPPORT_HOLD_MODE | SUPPORT_AUX_HEAT) + SUPPORT_HOLD_MODE | SUPPORT_AUX_HEAT | + SUPPORT_TARGET_TEMPERATURE_LOW | + SUPPORT_TARGET_TEMPERATURE_HIGH) assert state.attributes.get("supported_features") == support @@ -341,6 +346,66 @@ def test_set_target_temperature_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) assert 1701 == state.attributes.get('temperature') + def test_set_target_temperature_low_high(self): + """Test setting the low/high target temperature.""" + assert setup_component(self.hass, CLIMATE_DOMAIN, DEFAULT_CONFIG) + + common.set_temperature(self.hass, target_temp_low=20, + target_temp_high=23, + entity_id=ENTITY_CLIMATE) + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + print(state.attributes) + assert 20 == state.attributes.get('target_temp_low') + assert 23 == state.attributes.get('target_temp_high') + self.mock_publish.async_publish.assert_any_call( + 'temperature-low-topic', 20, 0, False) + self.mock_publish.async_publish.assert_any_call( + 'temperature-high-topic', 23, 0, False) + + def test_set_target_temperature_low_highpessimistic(self): + """Test setting the low/high target temperature.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config['climate']['temperature_low_state_topic'] = \ + 'temperature-low-state' + config['climate']['temperature_high_state_topic'] = \ + 'temperature-high-state' + assert setup_component(self.hass, CLIMATE_DOMAIN, config) + + state = self.hass.states.get(ENTITY_CLIMATE) + assert state.attributes.get('target_temp_low') is None + assert state.attributes.get('target_temp_high') is None + self.hass.block_till_done() + common.set_temperature(self.hass, target_temp_low=20, + target_temp_high=23, + entity_id=ENTITY_CLIMATE) + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + assert state.attributes.get('target_temp_low') is None + assert state.attributes.get('target_temp_high') is None + + fire_mqtt_message(self.hass, 'temperature-low-state', '1701') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + assert 1701 == state.attributes.get('target_temp_low') + assert state.attributes.get('target_temp_high') is None + + fire_mqtt_message(self.hass, 'temperature-high-state', '1703') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + assert 1701 == state.attributes.get('target_temp_low') + assert 1703 == state.attributes.get('target_temp_high') + + fire_mqtt_message(self.hass, 'temperature-low-state', 'not a number') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + assert 1701 == state.attributes.get('target_temp_low') + + fire_mqtt_message(self.hass, 'temperature-high-state', 'not a number') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + assert 1703 == state.attributes.get('target_temp_high') + def test_receive_mqtt_temperature(self): """Test getting the current temperature via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) From 6c53528ae8ae03f47e41ee56164183b2df0d8ab0 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Mon, 8 Apr 2019 06:43:38 -0700 Subject: [PATCH 511/605] Update harmony manifest to match REQUIREMENTS in module (#22826) --- homeassistant/components/harmony/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/harmony/manifest.json b/homeassistant/components/harmony/manifest.json index c82e9b7bf104d7..b2f9e69e014627 100644 --- a/homeassistant/components/harmony/manifest.json +++ b/homeassistant/components/harmony/manifest.json @@ -3,7 +3,7 @@ "name": "Harmony", "documentation": "https://www.home-assistant.io/components/harmony", "requirements": [ - "aioharmony==0.1.8" + "aioharmony==0.1.11" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index de505dec18f55a..76ec55ee3ce49f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -121,7 +121,7 @@ aiofreepybox==0.0.8 aioftp==0.12.0 # homeassistant.components.harmony -aioharmony==0.1.8 +aioharmony==0.1.11 # homeassistant.components.emulated_hue # homeassistant.components.http From 5727beed8e15597e3708e84bf8f583d598b42d64 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 8 Apr 2019 15:44:24 +0200 Subject: [PATCH 512/605] Add ESPHome Cover position/tilt support (#22858) ## Description: Add ESPHome cover position and tilt support. The aioesphomeapi also received a small refactor for these changes and those are part of this PR (constants were refactored into enums and optimistic was renamed to assumed_state). If possible, I'd like to include those in this PR because: 1. It's mostly just very simple changes 2. Because of the new position change the dev branch would be in a non-working state for a while until the split PR is merged (unless I write some temporary glue logic, but I'd prefer to avoid that) ## Checklist: - [x] The code change is tested and works locally. - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [x] There is no commented out code in this PR. If the code communicates with devices, web services, or third-party tools: - [x] [_The manifest file_][manifest-docs] has all fields filled out correctly ([example][ex-manifest]). - [x] New dependencies have been added to `requirements` in the manifest ([example][ex-requir]). - [x] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`. [ex-manifest]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json [ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mobile_app/manifest.json#L5 [ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard/__init__.py#L23 [manifest-docs]: https://developers.home-assistant.io/docs/en/development_checklist.html#_the-manifest-file_ --- homeassistant/components/esphome/__init__.py | 13 ++- homeassistant/components/esphome/cover.py | 89 +++++++++++++++---- homeassistant/components/esphome/fan.py | 33 ++++--- .../components/esphome/manifest.json | 4 +- homeassistant/components/esphome/switch.py | 2 +- requirements_all.txt | 2 +- 6 files changed, 105 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 39422c530b3f1d..19cd851002a284 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -32,7 +32,7 @@ ServiceCall, UserService DOMAIN = 'esphome' -REQUIREMENTS = ['aioesphomeapi==1.7.0'] +REQUIREMENTS = ['aioesphomeapi==2.0.0'] _LOGGER = logging.getLogger(__name__) @@ -381,16 +381,15 @@ async def _async_setup_device_registry(hass: HomeAssistantType, async def _register_service(hass: HomeAssistantType, entry_data: RuntimeEntryData, service: 'UserService'): - from aioesphomeapi import USER_SERVICE_ARG_BOOL, USER_SERVICE_ARG_INT, \ - USER_SERVICE_ARG_FLOAT, USER_SERVICE_ARG_STRING + from aioesphomeapi import UserServiceArgType service_name = '{}_{}'.format(entry_data.device_info.name, service.name) schema = {} for arg in service.args: schema[vol.Required(arg.name)] = { - USER_SERVICE_ARG_BOOL: cv.boolean, - USER_SERVICE_ARG_INT: vol.Coerce(int), - USER_SERVICE_ARG_FLOAT: vol.Coerce(float), - USER_SERVICE_ARG_STRING: cv.string, + UserServiceArgType.BOOL: cv.boolean, + UserServiceArgType.INT: vol.Coerce(int), + UserServiceArgType.FLOAT: vol.Coerce(float), + UserServiceArgType.STRING: cv.string, }[arg.type_] async def execute_service(call): diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index d86c40e627e354..68eb4221a931c9 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -3,7 +3,9 @@ from typing import TYPE_CHECKING, Optional from homeassistant.components.cover import ( - SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, CoverDevice) + ATTR_POSITION, ATTR_TILT_POSITION, SUPPORT_CLOSE, SUPPORT_CLOSE_TILT, + SUPPORT_OPEN, SUPPORT_OPEN_TILT, SUPPORT_SET_POSITION, + SUPPORT_SET_TILT_POSITION, SUPPORT_STOP, CoverDevice) from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType @@ -38,44 +40,97 @@ class EsphomeCover(EsphomeEntity, CoverDevice): def _static_info(self) -> 'CoverInfo': return super()._static_info - @property - def _state(self) -> Optional['CoverState']: - return super()._state - @property def supported_features(self) -> int: """Flag supported features.""" - return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + flags = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + if self._static_info.supports_position: + flags |= SUPPORT_SET_POSITION + if self._static_info.supports_tilt: + flags |= (SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | + SUPPORT_SET_TILT_POSITION) + return flags + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._static_info.device_class @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" - return self._static_info.is_optimistic + return self._static_info.assumed_state + + @property + def _state(self) -> Optional['CoverState']: + return super()._state @property def is_closed(self) -> Optional[bool]: """Return if the cover is closed or not.""" if self._state is None: return None - return bool(self._state.state) + # Check closed state with api version due to a protocol change + return self._state.is_closed(self._client.api_version) + + @property + def is_opening(self): + """Return if the cover is opening or not.""" + from aioesphomeapi import CoverOperation + if self._state is None: + return None + return self._state.current_operation == CoverOperation.IS_OPENING + + @property + def is_closing(self): + """Return if the cover is closing or not.""" + from aioesphomeapi import CoverOperation + if self._state is None: + return None + return self._state.current_operation == CoverOperation.IS_CLOSING + + @property + def current_cover_position(self) -> Optional[float]: + """Return current position of cover. 0 is closed, 100 is open.""" + if self._state is None or not self._static_info.supports_position: + return None + return self._state.position * 100.0 + + @property + def current_cover_tilt_position(self) -> Optional[float]: + """Return current position of cover tilt. 0 is closed, 100 is open.""" + if self._state is None or not self._static_info.supports_tilt: + return None + return self._state.tilt * 100.0 async def async_open_cover(self, **kwargs) -> None: """Open the cover.""" - from aioesphomeapi.client import COVER_COMMAND_OPEN - await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_OPEN) + position=1.0) async def async_close_cover(self, **kwargs) -> None: """Close cover.""" - from aioesphomeapi.client import COVER_COMMAND_CLOSE - await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_CLOSE) + position=0.0) - async def async_stop_cover(self, **kwargs): + async def async_stop_cover(self, **kwargs) -> None: """Stop the cover.""" - from aioesphomeapi.client import COVER_COMMAND_STOP + await self._client.cover_command(key=self._static_info.key, stop=True) + + async def async_set_cover_position(self, **kwargs) -> None: + """Move the cover to a specific position.""" + await self._client.cover_command(key=self._static_info.key, + position=kwargs[ATTR_POSITION] / 100) + + async def async_open_cover_tilt(self, **kwargs) -> None: + """Open the cover tilt.""" + await self._client.cover_command(key=self._static_info.key, tilt=1.0) + + async def async_close_cover_tilt(self, **kwargs) -> None: + """Close the cover tilt.""" + await self._client.cover_command(key=self._static_info.key, tilt=0.0) + async def async_set_cover_tilt_position(self, **kwargs) -> None: + """Move the cover tilt to a specific position.""" await self._client.cover_command(key=self._static_info.key, - command=COVER_COMMAND_STOP) + tilt=kwargs[ATTR_TILT_POSITION] / 100) diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index 05f18cb014acb2..973fa85774c0a9 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: # pylint: disable=unused-import - from aioesphomeapi import FanInfo, FanState # noqa + from aioesphomeapi import FanInfo, FanState, FanSpeed # noqa DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) @@ -32,12 +32,24 @@ async def async_setup_entry(hass: HomeAssistantType, ) -FAN_SPEED_STR_TO_INT = { - SPEED_LOW: 0, - SPEED_MEDIUM: 1, - SPEED_HIGH: 2 -} -FAN_SPEED_INT_TO_STR = {v: k for k, v in FAN_SPEED_STR_TO_INT.items()} +def _ha_fan_speed_to_esphome(speed: str) -> 'FanSpeed': + # pylint: disable=redefined-outer-name + from aioesphomeapi import FanSpeed # noqa + return { + SPEED_LOW: FanSpeed.LOW, + SPEED_MEDIUM: FanSpeed.MEDIUM, + SPEED_HIGH: FanSpeed.HIGH, + }[speed] + + +def _esphome_fan_speed_to_ha(speed: 'FanSpeed') -> str: + # pylint: disable=redefined-outer-name + from aioesphomeapi import FanSpeed # noqa + return { + FanSpeed.LOW: SPEED_LOW, + FanSpeed.MEDIUM: SPEED_MEDIUM, + FanSpeed.HIGH: SPEED_HIGH, + }[speed] class EsphomeFan(EsphomeEntity, FanEntity): @@ -56,8 +68,9 @@ async def async_set_speed(self, speed: str) -> None: if speed == SPEED_OFF: await self.async_turn_off() return + await self._client.fan_command( - self._static_info.key, speed=FAN_SPEED_STR_TO_INT[speed]) + self._static_info.key, speed=_ha_fan_speed_to_esphome(speed)) async def async_turn_on(self, speed: Optional[str] = None, **kwargs) -> None: @@ -67,7 +80,7 @@ async def async_turn_on(self, speed: Optional[str] = None, return data = {'key': self._static_info.key, 'state': True} if speed is not None: - data['speed'] = FAN_SPEED_STR_TO_INT[speed] + data['speed'] = _ha_fan_speed_to_esphome(speed) await self._client.fan_command(**data) # pylint: disable=arguments-differ @@ -94,7 +107,7 @@ def speed(self) -> Optional[str]: return None if not self._static_info.supports_speed: return None - return FAN_SPEED_INT_TO_STR[self._state.speed] + return _esphome_fan_speed_to_ha(self._state.speed) @property def oscillating(self) -> None: diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index b00cdf9607d90d..734544b49c7803 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -1,9 +1,9 @@ { "domain": "esphome", - "name": "Esphome", + "name": "ESPHome", "documentation": "https://www.home-assistant.io/components/esphome", "requirements": [ - "aioesphomeapi==1.7.0" + "aioesphomeapi==2.0.0" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/esphome/switch.py b/homeassistant/components/esphome/switch.py index e5a9d0cf4461f2..e736c1df2097f1 100644 --- a/homeassistant/components/esphome/switch.py +++ b/homeassistant/components/esphome/switch.py @@ -49,7 +49,7 @@ def icon(self) -> str: @property def assumed_state(self) -> bool: """Return true if we do optimistic updates.""" - return self._static_info.optimistic + return self._static_info.assumed_state @property def is_on(self): diff --git a/requirements_all.txt b/requirements_all.txt index 76ec55ee3ce49f..9709d7c87efdfc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -112,7 +112,7 @@ aiobotocore==0.10.2 aiodns==1.1.1 # homeassistant.components.esphome -aioesphomeapi==1.7.0 +aioesphomeapi==2.0.0 # homeassistant.components.freebox aiofreepybox==0.0.8 From 49a2f5a40b6476c4ce97735f943d46c783e0ef1d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 8 Apr 2019 17:47:40 +0200 Subject: [PATCH 513/605] Use dict[key] for required config keys and keys with default values. (#22832) --- homeassistant/components/mqtt/cover.py | 124 ++++++++++++------------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 08b6c2b74ba0cd..5cb7300f0efcb4 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -84,38 +84,36 @@ def validate_options(value): PLATFORM_SCHEMA = vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SET_POSITION_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_SET_POSITION_TEMPLATE): cv.template, - vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_GET_POSITION_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_STATE_TOPIC): mqtt.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_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_CLOSE, default=DEFAULT_PAYLOAD_CLOSE): cv.string, + vol.Optional(CONF_PAYLOAD_OPEN, default=DEFAULT_PAYLOAD_OPEN): cv.string, vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string, - vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string, + vol.Optional(CONF_POSITION_CLOSED, default=DEFAULT_POSITION_CLOSED): int, + vol.Optional(CONF_POSITION_OPEN, default=DEFAULT_POSITION_OPEN): int, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_SET_POSITION_TEMPLATE): cv.template, + vol.Optional(CONF_SET_POSITION_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string, - vol.Optional(CONF_POSITION_OPEN, - default=DEFAULT_POSITION_OPEN): int, - vol.Optional(CONF_POSITION_CLOSED, - default=DEFAULT_POSITION_CLOSED): int, - vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, - vol.Optional(CONF_TILT_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_TILT_STATUS_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string, + vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_TILT_CLOSED_POSITION, default=DEFAULT_TILT_CLOSED_POSITION): int, + vol.Optional(CONF_TILT_COMMAND_TOPIC): mqtt.valid_publish_topic, + vol.Optional(CONF_TILT_INVERT_STATE, + default=DEFAULT_TILT_INVERT_STATE): cv.boolean, + vol.Optional(CONF_TILT_MAX, default=DEFAULT_TILT_MAX): int, + vol.Optional(CONF_TILT_MIN, default=DEFAULT_TILT_MIN): int, vol.Optional(CONF_TILT_OPEN_POSITION, default=DEFAULT_TILT_OPEN_POSITION): int, - vol.Optional(CONF_TILT_MIN, default=DEFAULT_TILT_MIN): int, - vol.Optional(CONF_TILT_MAX, default=DEFAULT_TILT_MAX): int, vol.Optional(CONF_TILT_STATE_OPTIMISTIC, default=DEFAULT_TILT_OPTIMISTIC): cv.boolean, - vol.Optional(CONF_TILT_INVERT_STATE, - default=DEFAULT_TILT_INVERT_STATE): cv.boolean, + vol.Optional(CONF_TILT_STATUS_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema), validate_options) @@ -194,10 +192,10 @@ async def discovery_update(self, discovery_payload): def _setup_from_config(self, config): self._config = config - self._optimistic = (config.get(CONF_OPTIMISTIC) or + self._optimistic = (config[CONF_OPTIMISTIC] or (config.get(CONF_STATE_TOPIC) is None and config.get(CONF_GET_POSITION_TOPIC) is None)) - self._tilt_optimistic = config.get(CONF_TILT_STATE_OPTIMISTIC) + self._tilt_optimistic = config[CONF_TILT_STATE_OPTIMISTIC] async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -214,8 +212,8 @@ async def _subscribe_topics(self): def tilt_updated(msg): """Handle tilt updates.""" if (msg.payload.isnumeric() and - (self._config.get(CONF_TILT_MIN) <= int(msg.payload) <= - self._config.get(CONF_TILT_MAX))): + (self._config[CONF_TILT_MIN] <= int(msg.payload) <= + self._config[CONF_TILT_MAX])): level = self.find_percentage_in_range(float(msg.payload)) self._tilt_value = level @@ -229,9 +227,9 @@ def state_message_received(msg): payload = template.async_render_with_possible_json_value( payload) - if payload == self._config.get(CONF_STATE_OPEN): + if payload == self._config[CONF_STATE_OPEN]: self._state = False - elif payload == self._config.get(CONF_STATE_CLOSED): + elif payload == self._config[CONF_STATE_CLOSED]: self._state = True else: _LOGGER.warning("Payload is not True or False: %s", payload) @@ -262,12 +260,12 @@ def position_message_received(msg): topics['get_position_topic'] = { 'topic': self._config.get(CONF_GET_POSITION_TOPIC), 'msg_callback': position_message_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} elif self._config.get(CONF_STATE_TOPIC): topics['state_topic'] = { 'topic': self._config.get(CONF_STATE_TOPIC), 'msg_callback': state_message_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} else: # Force into optimistic mode. self._optimistic = True @@ -280,7 +278,7 @@ def position_message_received(msg): topics['tilt_status_topic'] = { 'topic': self._config.get(CONF_TILT_STATUS_TOPIC), 'msg_callback': tilt_updated, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._sub_state = await subscription.async_subscribe_topics( self.hass, self._sub_state, @@ -306,7 +304,7 @@ def assumed_state(self): @property def name(self): """Return the name of the cover.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def is_closed(self): @@ -353,14 +351,14 @@ async def async_open_cover(self, **kwargs): """ mqtt.async_publish( self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_OPEN), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_OPEN], self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that cover has changed state. self._state = False if self._config.get(CONF_GET_POSITION_TOPIC): self._position = self.find_percentage_in_range( - self._config.get(CONF_POSITION_OPEN), COVER_PAYLOAD) + self._config[CONF_POSITION_OPEN], COVER_PAYLOAD) self.async_write_ha_state() async def async_close_cover(self, **kwargs): @@ -370,14 +368,14 @@ async def async_close_cover(self, **kwargs): """ mqtt.async_publish( self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_CLOSE), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_CLOSE], self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that cover has changed state. self._state = True if self._config.get(CONF_GET_POSITION_TOPIC): self._position = self.find_percentage_in_range( - self._config.get(CONF_POSITION_CLOSED), COVER_PAYLOAD) + self._config[CONF_POSITION_CLOSED], COVER_PAYLOAD) self.async_write_ha_state() async def async_stop_cover(self, **kwargs): @@ -387,29 +385,29 @@ async def async_stop_cover(self, **kwargs): """ mqtt.async_publish( self.hass, self._config.get(CONF_COMMAND_TOPIC), - self._config.get(CONF_PAYLOAD_STOP), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_PAYLOAD_STOP], self._config[CONF_QOS], + self._config[CONF_RETAIN]) async def async_open_cover_tilt(self, **kwargs): """Tilt the cover open.""" mqtt.async_publish(self.hass, self._config.get(CONF_TILT_COMMAND_TOPIC), - self._config.get(CONF_TILT_OPEN_POSITION), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_TILT_OPEN_POSITION], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._tilt_optimistic: - self._tilt_value = self._config.get(CONF_TILT_OPEN_POSITION) + self._tilt_value = self._config[CONF_TILT_OPEN_POSITION] self.async_write_ha_state() async def async_close_cover_tilt(self, **kwargs): """Tilt the cover closed.""" mqtt.async_publish(self.hass, self._config.get(CONF_TILT_COMMAND_TOPIC), - self._config.get(CONF_TILT_CLOSED_POSITION), - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_TILT_CLOSED_POSITION], + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._tilt_optimistic: - self._tilt_value = self._config.get(CONF_TILT_CLOSED_POSITION) + self._tilt_value = self._config[CONF_TILT_CLOSED_POSITION] self.async_write_ha_state() async def async_set_cover_tilt_position(self, **kwargs): @@ -425,8 +423,8 @@ async def async_set_cover_tilt_position(self, **kwargs): mqtt.async_publish(self.hass, self._config.get(CONF_TILT_COMMAND_TOPIC), level, - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], + self._config[CONF_RETAIN]) async def async_set_cover_position(self, **kwargs): """Move the cover to a specific position.""" @@ -441,19 +439,19 @@ async def async_set_cover_position(self, **kwargs): except TemplateError as ex: _LOGGER.error(ex) self._state = None - elif (self._config.get(CONF_POSITION_OPEN) != 100 and - self._config.get(CONF_POSITION_CLOSED) != 0): + elif (self._config[CONF_POSITION_OPEN] != 100 and + self._config[CONF_POSITION_CLOSED] != 0): position = self.find_in_range_from_percent( position, COVER_PAYLOAD) mqtt.async_publish(self.hass, self._config.get(CONF_SET_POSITION_TOPIC), position, - self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic: self._state = percentage_position == \ - self._config.get(CONF_POSITION_CLOSED) + self._config[CONF_POSITION_CLOSED] self._position = percentage_position self.async_write_ha_state() @@ -461,11 +459,11 @@ def find_percentage_in_range(self, position, range_type=TILT_PAYLOAD): """Find the 0-100% value within the specified range.""" # the range of motion as defined by the min max values if range_type == COVER_PAYLOAD: - max_range = self._config.get(CONF_POSITION_OPEN) - min_range = self._config.get(CONF_POSITION_CLOSED) + max_range = self._config[CONF_POSITION_OPEN] + min_range = self._config[CONF_POSITION_CLOSED] else: - max_range = self._config.get(CONF_TILT_MAX) - min_range = self._config.get(CONF_TILT_MIN) + max_range = self._config[CONF_TILT_MAX] + min_range = self._config[CONF_TILT_MIN] current_range = max_range - min_range # offset to be zero based offset_position = position - min_range @@ -477,7 +475,7 @@ def find_percentage_in_range(self, position, range_type=TILT_PAYLOAD): position_percentage = min(max(position_percentage, min_percent), max_percent) if range_type == TILT_PAYLOAD and \ - self._config.get(CONF_TILT_INVERT_STATE): + self._config[CONF_TILT_INVERT_STATE]: return 100 - position_percentage return position_percentage @@ -491,18 +489,18 @@ def find_in_range_from_percent(self, percentage, range_type=TILT_PAYLOAD): returning the offset """ if range_type == COVER_PAYLOAD: - max_range = self._config.get(CONF_POSITION_OPEN) - min_range = self._config.get(CONF_POSITION_CLOSED) + max_range = self._config[CONF_POSITION_OPEN] + min_range = self._config[CONF_POSITION_CLOSED] else: - max_range = self._config.get(CONF_TILT_MAX) - min_range = self._config.get(CONF_TILT_MIN) + max_range = self._config[CONF_TILT_MAX] + min_range = self._config[CONF_TILT_MIN] offset = min_range current_range = max_range - min_range position = round(current_range * (percentage / 100.0)) position += offset if range_type == TILT_PAYLOAD and \ - self._config.get(CONF_TILT_INVERT_STATE): + self._config[CONF_TILT_INVERT_STATE]: position = max_range - position + offset return position From 55c8417ec0530a9c014266215e9798188231e237 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 8 Apr 2019 19:08:03 +0200 Subject: [PATCH 514/605] fix aiohttp ServerDisconnectedError in Daikin (#22880) --- homeassistant/components/daikin/__init__.py | 17 ++++++++++------- homeassistant/components/daikin/config_flow.py | 8 ++++++-- homeassistant/components/daikin/manifest.json | 2 +- requirements_all.txt | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 5ad21f5954f3b6..2df831eb6dbed5 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -2,13 +2,13 @@ import asyncio from datetime import timedelta import logging -from socket import timeout -import async_timeout +from aiohttp import ClientConnectionError +from async_timeout import timeout import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import CONF_HOSTS, CONF_HOST +from homeassistant.const import CONF_HOST, CONF_HOSTS import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.typing import HomeAssistantType @@ -16,7 +16,7 @@ from . import config_flow # noqa pylint_disable=unused-import -REQUIREMENTS = ['pydaikin==1.3.1'] +REQUIREMENTS = ['pydaikin==1.4.0'] _LOGGER = logging.getLogger(__name__) @@ -88,11 +88,14 @@ async def daikin_api_setup(hass, host): from pydaikin.appliance import Appliance session = hass.helpers.aiohttp_client.async_get_clientsession() try: - with async_timeout.timeout(10): + with timeout(10, loop=hass.loop): device = Appliance(host, session) await device.init() except asyncio.TimeoutError: - _LOGGER.error("Connection to Daikin could not be established") + _LOGGER.error("Connection to Daikin timeout") + return None + except ClientConnectionError: + _LOGGER.error("ServerDisconected") return None except Exception: # pylint: disable=broad-except _LOGGER.error("Unexpected error creating device") @@ -119,7 +122,7 @@ async def async_update(self, **kwargs): try: await self.device.update_status() self._available = True - except timeout: + except ClientConnectionError: _LOGGER.warning( "Connection failed for %s", self.ip_address ) diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index 3c5daac4653be9..7c214e77050f0f 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -2,7 +2,8 @@ import asyncio import logging -import async_timeout +from aiohttp import ClientError +from async_timeout import timeout import voluptuous as vol from homeassistant import config_entries @@ -42,10 +43,13 @@ async def _create_device(self, host): host, self.hass.helpers.aiohttp_client.async_get_clientsession(), ) - with async_timeout.timeout(10): + with timeout(10): await device.init() except asyncio.TimeoutError: return self.async_abort(reason='device_timeout') + except ClientError: + _LOGGER.exception("ClientError") + return self.async_abort(reason='device_fail') except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected error creating device") return self.async_abort(reason='device_fail') diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json index 28314bdf084bfe..ab842950e2488e 100644 --- a/homeassistant/components/daikin/manifest.json +++ b/homeassistant/components/daikin/manifest.json @@ -3,7 +3,7 @@ "name": "Daikin", "documentation": "https://www.home-assistant.io/components/daikin", "requirements": [ - "pydaikin==1.3.1" + "pydaikin==1.4.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 9709d7c87efdfc..e46f9dd40194fc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -985,7 +985,7 @@ pycsspeechtts==1.0.2 # pycups==1.9.73 # homeassistant.components.daikin -pydaikin==1.3.1 +pydaikin==1.4.0 # homeassistant.components.danfoss_air pydanfossair==0.0.7 From d577955d1e4ae0b7ad7b308a034851b9e114e7ef Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 9 Apr 2019 02:32:56 +0200 Subject: [PATCH 515/605] Fix Sonos handling of unsupported favorites (#22906) --- homeassistant/components/sonos/__init__.py | 2 +- homeassistant/components/sonos/manifest.json | 2 +- homeassistant/components/sonos/media_player.py | 13 +------------ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index e9f297e4f079f7..9c9914b787b854 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -4,7 +4,7 @@ DOMAIN = 'sonos' -REQUIREMENTS = ['pysonos==0.0.8'] +REQUIREMENTS = ['pysonos==0.0.9'] async def async_setup(hass, config): diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index 3fa5ac0354a6da..79cc76539378fe 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,7 +3,7 @@ "name": "Sonos", "documentation": "https://www.home-assistant.io/components/sonos", "requirements": [ - "pysonos==0.0.8" + "pysonos==0.0.9" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index ba7854e4f0d28f..7c2e5fec843075 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -454,18 +454,7 @@ def _set_basic_information(self): def _set_favorites(self): """Set available favorites.""" - # SoCo 0.16 raises a generic Exception on invalid xml in favorites. - # Filter those out now so our list is safe to use. - try: - self._favorites = [] - for fav in self.soco.music_library.get_sonos_favorites(): - try: - if fav.reference.get_uri(): - self._favorites.append(fav) - except Exception: # pylint: disable=broad-except - _LOGGER.debug("Ignoring invalid favorite '%s'", fav.title) - except Exception: # pylint: disable=broad-except - _LOGGER.debug("Ignoring invalid favorite list") + self._favorites = self.soco.music_library.get_sonos_favorites() def _radio_artwork(self, url): """Return the private URL with artwork for a radio stream.""" diff --git a/requirements_all.txt b/requirements_all.txt index e46f9dd40194fc..e61530ce77ddf7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1270,7 +1270,7 @@ pysmartthings==0.6.7 pysnmp==4.4.8 # homeassistant.components.sonos -pysonos==0.0.8 +pysonos==0.0.9 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c9890f92626591..e2935369d92c79 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -239,7 +239,7 @@ pysmartapp==0.3.2 pysmartthings==0.6.7 # homeassistant.components.sonos -pysonos==0.0.8 +pysonos==0.0.9 # homeassistant.components.spc pyspcwebgw==0.4.0 From 38f063a1586f1587886b11b763bd51e5538e06ca Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Mon, 8 Apr 2019 21:24:40 -0500 Subject: [PATCH 516/605] Fix HEOS discovery could result in multiple config entries (#22903) * Prevent duplicate entries from discovery * Update reqs files * Prevent duplicate entries from discovery --- homeassistant/components/heos/__init__.py | 2 +- homeassistant/components/heos/config_flow.py | 9 ++- homeassistant/components/heos/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/heos/conftest.py | 18 ++++++ tests/components/heos/test_config_flow.py | 62 ++++++++++++-------- 7 files changed, 68 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index dadd9f1046445d..ffbd8ebffd45f2 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -19,7 +19,7 @@ COMMAND_RETRY_ATTEMPTS, COMMAND_RETRY_DELAY, DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) -REQUIREMENTS = ['pyheos==0.3.0'] +REQUIREMENTS = ['pyheos==0.3.1'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ diff --git a/homeassistant/components/heos/config_flow.py b/homeassistant/components/heos/config_flow.py index 7ccb43c60e9e62..66650531cadfaa 100644 --- a/homeassistant/components/heos/config_flow.py +++ b/homeassistant/components/heos/config_flow.py @@ -23,8 +23,13 @@ class HeosFlowHandler(config_entries.ConfigFlow): async def async_step_discovery(self, discovery_info): """Handle a discovered Heos device.""" - return await self.async_step_user( - {CONF_HOST: discovery_info[CONF_HOST]}) + # Only continue if this is the only active flow + flows = self.hass.config_entries.flow.async_progress() + heos_flows = [flow for flow in flows if flow['handler'] == DOMAIN] + if len(heos_flows) == 1: + return await self.async_step_user( + {CONF_HOST: discovery_info[CONF_HOST]}) + return self.async_abort(reason='already_setup') async def async_step_import(self, user_input=None): """Occurs when an entry is setup through config.""" diff --git a/homeassistant/components/heos/manifest.json b/homeassistant/components/heos/manifest.json index 91cefed75f7d12..2977345f97d0ef 100644 --- a/homeassistant/components/heos/manifest.json +++ b/homeassistant/components/heos/manifest.json @@ -3,7 +3,7 @@ "name": "Heos", "documentation": "https://www.home-assistant.io/components/heos", "requirements": [ - "pyheos==0.3.0" + "pyheos==0.3.1" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index e61530ce77ddf7..b64079b4460d98 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1064,7 +1064,7 @@ pygtt==1.1.2 pyhaversion==2.0.3 # homeassistant.components.heos -pyheos==0.3.0 +pyheos==0.3.1 # homeassistant.components.hikvision pyhik==0.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e2935369d92c79..6e70c636314a08 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -204,7 +204,7 @@ pydeconz==54 pydispatcher==2.0.5 # homeassistant.components.heos -pyheos==0.3.0 +pyheos==0.3.1 # homeassistant.components.homematic pyhomematic==0.1.58 diff --git a/tests/components/heos/conftest.py b/tests/components/heos/conftest.py index 1b76db21187db3..db675a24ee095f 100644 --- a/tests/components/heos/conftest.py +++ b/tests/components/heos/conftest.py @@ -103,3 +103,21 @@ def input_sources_fixture() -> Sequence[InputSource]: def dispatcher_fixture() -> Dispatcher: """Create a dispatcher for testing.""" return Dispatcher() + + +@pytest.fixture(name="discovery_data") +def discovery_data_fixture() -> dict: + """Return mock discovery data for testing.""" + return { + 'host': '127.0.0.1', + 'manufacturer': 'Denon', + 'model_name': 'HEOS Drive', + 'model_number': 'DWSA-10 4.0', + 'name': 'Office', + 'port': 60006, + 'serial': None, + 'ssdp_description': + 'http://127.0.0.1:60006/upnp/desc/aios_device/aios_device.xml', + 'udn': 'uuid:e61de70c-2250-1c22-0080-0005cdf512be', + 'upnp_device_type': 'urn:schemas-denon-com:device:AiosDevice:1' + } diff --git a/tests/components/heos/test_config_flow.py b/tests/components/heos/test_config_flow.py index ddb2bd39384a69..9c33cbee7aaae9 100644 --- a/tests/components/heos/test_config_flow.py +++ b/tests/components/heos/test_config_flow.py @@ -3,6 +3,7 @@ from homeassistant import data_entry_flow from homeassistant.components.heos.config_flow import HeosFlowHandler +from homeassistant.components.heos.const import DOMAIN from homeassistant.const import CONF_HOST @@ -57,26 +58,41 @@ async def test_create_entry_when_host_valid(hass, controller): assert controller.disconnect.call_count == 1 -async def test_create_entry_with_discovery(hass, controller): - """Test result type is create entry when valid through discovery.""" - flow = HeosFlowHandler() - flow.hass = hass - data = { - 'host': '127.0.0.1', - 'manufacturer': 'Denon', - 'model_name': 'HEOS Drive', - 'model_number': 'DWSA-10 4.0', - 'name': 'Office', - 'port': 60006, - 'serial': None, - 'ssdp_description': - 'http://127.0.0.1:60006/upnp/desc/aios_device/aios_device.xml', - 'udn': 'uuid:e61de70c-2250-1c22-0080-0005cdf512be', - 'upnp_device_type': 'urn:schemas-denon-com:device:AiosDevice:1' - } - result = await flow.async_step_discovery(data) - assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result['title'] == 'Controller (127.0.0.1)' - assert result['data'] == {'host': '127.0.0.1'} - assert controller.connect.call_count == 1 - assert controller.disconnect.call_count == 1 +async def test_create_entry_with_discovery(hass, controller, discovery_data): + """Test discovery creates entry.""" + await hass.config_entries.flow.async_init( + DOMAIN, context={'source': 'discovery'}, + data=discovery_data) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0].data == {CONF_HOST: discovery_data[CONF_HOST]} + assert entries[0].title == 'Controller (127.0.0.1)' + + +async def test_entry_already_exists_discovery( + hass, controller, discovery_data, config_entry): + """Test discovery does not create multiple entries when already setup.""" + config_entry.add_to_hass(hass) + await hass.config_entries.flow.async_init( + DOMAIN, context={'source': 'discovery'}, + data=discovery_data) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + + +async def test_multiple_discovery_creates_single_entry( + hass, controller, discovery_data): + """Test discovery of multiple devices creates a single entry.""" + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={'source': 'discovery'}, + data={CONF_HOST: discovery_data})) + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={'source': 'discovery'}, + data={CONF_HOST: discovery_data})) + await hass.async_block_till_done() + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 From 1a05f7b04d33bf09af6a32ea760e99b7d9038da0 Mon Sep 17 00:00:00 2001 From: pbalogh77 Date: Tue, 9 Apr 2019 06:24:57 +0200 Subject: [PATCH 517/605] Initial Fibaro HC Climate support (#20256) * Initial version of climate * initial commit of climate device support * Fixed opmode and fanmode * Cleanup * meh * added back all other components Oops * wider support for thermostats Added one more identifier for thermostats to broaden compatibility * Added even more climate types * Reworked detection mechanism Better support for combined devices * Added additional modes * force visibility on climate * Changed logging of device data * Improved operatingmode support Improved operatingmode support * Updated logic for opmode/fanmode list creation Implemented a universal mapping logic for opmode and fanmode, to make it more widely compatible Improved mapping of Fibaro FGT devices * Lint fixes * bump * Fixes based on code review * Fixes * Moved to fibaro folder * lint inspired cosmetic changes * Mapped all operating modes to existing HA ones Mapped all operating modes to existing HA ones * Improved compatibility with Heatit thermostats Thanks to astrandb for testing, debugging and fixing my code * Changes based on code review Changes based on code review * more fixes based on more code review more fixes based on more code review --- homeassistant/components/fibaro/__init__.py | 51 ++-- homeassistant/components/fibaro/climate.py | 291 ++++++++++++++++++++ 2 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/fibaro/climate.py diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index 9a6ccccb5e3fbc..6b37b178a595bb 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -26,20 +26,11 @@ CONF_GATEWAYS = 'gateways' CONF_PLUGINS = 'plugins' CONF_RESET_COLOR = 'reset_color' - DOMAIN = 'fibaro' - FIBARO_CONTROLLERS = 'fibaro_controllers' FIBARO_DEVICES = 'fibaro_devices' - -FIBARO_COMPONENTS = [ - 'binary_sensor', - 'cover', - 'light', - 'scene', - 'sensor', - 'switch', -] +FIBARO_COMPONENTS = ['binary_sensor', 'climate', 'cover', 'light', + 'scene', 'sensor', 'switch'] FIBARO_TYPEMAP = { 'com.fibaro.multilevelSensor': "sensor", @@ -56,7 +47,11 @@ 'com.fibaro.remoteSwitch': 'switch', 'com.fibaro.sensor': 'sensor', 'com.fibaro.colorController': 'light', - 'com.fibaro.securitySensor': 'binary_sensor' + 'com.fibaro.securitySensor': 'binary_sensor', + 'com.fibaro.hvac': 'climate', + 'com.fibaro.setpoint': 'climate', + 'com.fibaro.FGT001': 'climate', + 'com.fibaro.thermostatDanfoss': 'climate' } DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({ @@ -174,6 +169,16 @@ def register(self, device_id, callback): """Register device with a callback for updates.""" self._callbacks[device_id] = callback + def get_children(self, device_id): + """Get a list of child devices.""" + return [ + device for device in self._device_map.values() + if device.parentId == device_id] + + def get_siblings(self, device_id): + """Get the siblings of a device.""" + return self.get_children(self._device_map[device_id].parentId) + @staticmethod def _map_device_to_type(device): """Map device to HA device type.""" @@ -229,6 +234,7 @@ def _read_devices(self): devices = self._client.devices.list() self._device_map = {} self.fibaro_devices = defaultdict(list) + last_climate_parent = None for device in devices: try: device.fibaro_controller = self @@ -249,15 +255,26 @@ def _read_devices(self): self._device_config.get(device.ha_id, {}) else: device.mapped_type = None - if device.mapped_type: + dtype = device.mapped_type + if dtype: device.unique_id_str = "{}.{}".format( self.hub_serial, device.id) self._device_map[device.id] = device - self.fibaro_devices[device.mapped_type].append(device) - _LOGGER.debug("%s (%s, %s) -> %s. Prop: %s Actions: %s", + if dtype != 'climate': + self.fibaro_devices[dtype].append(device) + else: + # if a sibling of this has been added, skip this one + # otherwise add the first visible device in the group + # which is a hack, but solves a problem with FGT having + # hidden compatibility devices before the real device + if last_climate_parent != device.parentId and \ + device.visible: + self.fibaro_devices[dtype].append(device) + last_climate_parent = device.parentId + _LOGGER.debug("%s (%s, %s) -> %s %s", device.ha_id, device.type, - device.baseType, device.mapped_type, - str(device.properties), str(device.actions)) + device.baseType, dtype, + str(device)) except (KeyError, ValueError): pass diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py new file mode 100644 index 00000000000000..0d1ecc3a77f9cf --- /dev/null +++ b/homeassistant/components/fibaro/climate.py @@ -0,0 +1,291 @@ +"""Support for Fibaro thermostats.""" +import logging + +from homeassistant.components.climate.const import ( + STATE_AUTO, STATE_COOL, STATE_DRY, + STATE_ECO, STATE_FAN_ONLY, STATE_HEAT, + STATE_MANUAL, SUPPORT_TARGET_TEMPERATURE, + SUPPORT_OPERATION_MODE, SUPPORT_FAN_MODE) + +from homeassistant.components.climate import ( + ClimateDevice) + +from homeassistant.const import ( + ATTR_TEMPERATURE, + STATE_OFF, + TEMP_CELSIUS, + TEMP_FAHRENHEIT) + +from . import ( + FIBARO_DEVICES, FibaroDevice) + +SPEED_LOW = 'low' +SPEED_MEDIUM = 'medium' +SPEED_HIGH = 'high' + +# State definitions missing from HA, but defined by Z-Wave standard. +# We map them to states known supported by HA here: +STATE_AUXILIARY = STATE_HEAT +STATE_RESUME = STATE_HEAT +STATE_MOIST = STATE_DRY +STATE_AUTO_CHANGEOVER = STATE_AUTO +STATE_ENERGY_HEAT = STATE_ECO +STATE_ENERGY_COOL = STATE_COOL +STATE_FULL_POWER = STATE_AUTO +STATE_FORCE_OPEN = STATE_MANUAL +STATE_AWAY = STATE_AUTO +STATE_FURNACE = STATE_HEAT + +FAN_AUTO_HIGH = 'auto_high' +FAN_AUTO_MEDIUM = 'auto_medium' +FAN_CIRCULATION = 'circulation' +FAN_HUMIDITY_CIRCULATION = 'humidity_circulation' +FAN_LEFT_RIGHT = 'left_right' +FAN_UP_DOWN = 'up_down' +FAN_QUIET = 'quiet' + +DEPENDENCIES = ['fibaro'] + +_LOGGER = logging.getLogger(__name__) + +# SDS13781-10 Z-Wave Application Command Class Specification 2019-01-04 +# Table 128, Thermostat Fan Mode Set version 4::Fan Mode encoding +FANMODES = { + 0: STATE_OFF, + 1: SPEED_LOW, + 2: FAN_AUTO_HIGH, + 3: SPEED_HIGH, + 4: FAN_AUTO_MEDIUM, + 5: SPEED_MEDIUM, + 6: FAN_CIRCULATION, + 7: FAN_HUMIDITY_CIRCULATION, + 8: FAN_LEFT_RIGHT, + 9: FAN_UP_DOWN, + 10: FAN_QUIET, + 128: STATE_AUTO +} + +# SDS13781-10 Z-Wave Application Command Class Specification 2019-01-04 +# Table 130, Thermostat Mode Set version 3::Mode encoding. +OPMODES = { + 0: STATE_OFF, + 1: STATE_HEAT, + 2: STATE_COOL, + 3: STATE_AUTO, + 4: STATE_AUXILIARY, + 5: STATE_RESUME, + 6: STATE_FAN_ONLY, + 7: STATE_FURNACE, + 8: STATE_DRY, + 9: STATE_MOIST, + 10: STATE_AUTO_CHANGEOVER, + 11: STATE_ENERGY_HEAT, + 12: STATE_ENERGY_COOL, + 13: STATE_AWAY, + 15: STATE_FULL_POWER, + 31: STATE_FORCE_OPEN +} + +SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Perform the setup for Fibaro controller devices.""" + if discovery_info is None: + return + + add_entities( + [FibaroThermostat(device) + for device in hass.data[FIBARO_DEVICES]['climate']], True) + + +class FibaroThermostat(FibaroDevice, ClimateDevice): + """Representation of a Fibaro Thermostat.""" + + def __init__(self, fibaro_device): + """Initialize the Fibaro device.""" + super().__init__(fibaro_device) + self._temp_sensor_device = None + self._target_temp_device = None + self._op_mode_device = None + self._fan_mode_device = None + self._support_flags = 0 + self.entity_id = 'climate.{}'.format(self.ha_id) + self._fan_mode_to_state = {} + self._fan_state_to_mode = {} + self._op_mode_to_state = {} + self._op_state_to_mode = {} + + siblings = fibaro_device.fibaro_controller.get_siblings( + fibaro_device.id) + tempunit = 'C' + for device in siblings: + if device.type == 'com.fibaro.temperatureSensor': + self._temp_sensor_device = FibaroDevice(device) + tempunit = device.properties.unit + if 'setTargetLevel' in device.actions or \ + 'setThermostatSetpoint' in device.actions: + self._target_temp_device = FibaroDevice(device) + self._support_flags |= SUPPORT_TARGET_TEMPERATURE + tempunit = device.properties.unit + if 'setMode' in device.actions or \ + 'setOperatingMode' in device.actions: + self._op_mode_device = FibaroDevice(device) + self._support_flags |= SUPPORT_OPERATION_MODE + if 'setFanMode' in device.actions: + self._fan_mode_device = FibaroDevice(device) + self._support_flags |= SUPPORT_FAN_MODE + + if tempunit == 'F': + self._unit_of_temp = TEMP_FAHRENHEIT + else: + self._unit_of_temp = TEMP_CELSIUS + + if self._fan_mode_device: + fan_modes = self._fan_mode_device.fibaro_device.\ + properties.supportedModes.split(",") + for mode in fan_modes: + try: + self._fan_mode_to_state[int(mode)] = FANMODES[int(mode)] + self._fan_state_to_mode[FANMODES[int(mode)]] = int(mode) + except KeyError: + self._fan_mode_to_state[int(mode)] = 'unknown' + + if self._op_mode_device: + prop = self._op_mode_device.fibaro_device.properties + if "supportedOperatingModes" in prop: + op_modes = prop.supportedOperatingModes.split(",") + elif "supportedModes" in prop: + op_modes = prop.supportedModes.split(",") + for mode in op_modes: + try: + self._op_mode_to_state[int(mode)] = OPMODES[int(mode)] + self._op_state_to_mode[OPMODES[int(mode)]] = int(mode) + except KeyError: + self._op_mode_to_state[int(mode)] = 'unknown' + + async def async_added_to_hass(self): + """Call when entity is added to hass.""" + _LOGGER.debug("Climate %s\n" + "- _temp_sensor_device %s\n" + "- _target_temp_device %s\n" + "- _op_mode_device %s\n" + "- _fan_mode_device %s", + self.ha_id, + self._temp_sensor_device.ha_id + if self._temp_sensor_device else "None", + self._target_temp_device.ha_id + if self._target_temp_device else "None", + self._op_mode_device.ha_id + if self._op_mode_device else "None", + self._fan_mode_device.ha_id + if self._fan_mode_device else "None") + await super().async_added_to_hass() + + # Register update callback for child devices + siblings = self.fibaro_device.fibaro_controller.get_siblings( + self.fibaro_device.id) + for device in siblings: + if device != self.fibaro_device: + self.controller.register(device.id, + self._update_callback) + + @property + def supported_features(self): + """Return the list of supported features.""" + return self._support_flags + + @property + def fan_list(self): + """Return the list of available fan modes.""" + if self._fan_mode_device is None: + return None + return list(self._fan_state_to_mode) + + @property + def current_fan_mode(self): + """Return the fan setting.""" + if self._fan_mode_device is None: + return None + + mode = int(self._fan_mode_device.fibaro_device.properties.mode) + return self._fan_mode_to_state[mode] + + def set_fan_mode(self, fan_mode): + """Set new target fan mode.""" + if self._fan_mode_device is None: + return + self._fan_mode_device.action( + "setFanMode", self._fan_state_to_mode[fan_mode]) + + @property + def current_operation(self): + """Return current operation ie. heat, cool, idle.""" + if self._op_mode_device is None: + return None + + if "operatingMode" in self._op_mode_device.fibaro_device.properties: + mode = int(self._op_mode_device.fibaro_device. + properties.operatingMode) + else: + mode = int(self._op_mode_device.fibaro_device.properties.mode) + return self._op_mode_to_state.get(mode) + + @property + def operation_list(self): + """Return the list of available operation modes.""" + if self._op_mode_device is None: + return None + return list(self._op_state_to_mode) + + def set_operation_mode(self, operation_mode): + """Set new target operation mode.""" + if self._op_mode_device is None: + return + if "setOperatingMode" in self._op_mode_device.fibaro_device.actions: + self._op_mode_device.action( + "setOperatingMode", self._op_state_to_mode[operation_mode]) + elif "setMode" in self._op_mode_device.fibaro_device.actions: + self._op_mode_device.action( + "setMode", self._op_state_to_mode[operation_mode]) + + @property + def temperature_unit(self): + """Return the unit of measurement.""" + return self._unit_of_temp + + @property + def current_temperature(self): + """Return the current temperature.""" + if self._temp_sensor_device: + device = self._temp_sensor_device.fibaro_device + return float(device.properties.value) + return None + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + if self._target_temp_device: + device = self._target_temp_device.fibaro_device + return float(device.properties.targetLevel) + return None + + def set_temperature(self, **kwargs): + """Set new target temperatures.""" + temperature = kwargs.get(ATTR_TEMPERATURE) + target = self._target_temp_device + if temperature is not None: + if "setThermostatSetpoint" in target.fibaro_device.actions: + target.action("setThermostatSetpoint", + self._op_state_to_mode[self.current_operation], + temperature) + else: + target.action("setTargetLevel", + temperature) + + @property + def is_on(self): + """Return true if on.""" + if self.current_operation == STATE_OFF: + return False + return True From 590eead128835e1b693ebb6b3020f59ef19fc4bd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 8 Apr 2019 23:16:55 -0700 Subject: [PATCH 518/605] Test fixes (#22911) * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix more tests and manifests * Fix demo/notify tests * Rollback test for demo.geo_location --- .../components/ffmpeg_motion/manifest.json | 2 +- .../components/ffmpeg_noise/manifest.json | 2 +- homeassistant/components/html5/manifest.json | 2 +- homeassistant/components/meraki/manifest.json | 2 +- .../microsoft_face_detect/manifest.json | 2 +- .../microsoft_face_identify/manifest.json | 2 +- homeassistant/components/push/manifest.json | 2 +- tests/common.py | 10 ++++---- tests/components/demo/test_geo_location.py | 4 +++- tests/components/demo/test_notify.py | 1 + tests/components/ffmpeg/test_sensor.py | 6 +++++ tests/components/geofency/test_init.py | 23 ++++++++++--------- tests/components/gpslogger/test_init.py | 22 ++++++++++-------- tests/components/html5/test_notify.py | 1 + tests/components/light/test_init.py | 8 +++---- tests/components/locative/test_init.py | 10 ++++---- tests/components/mqtt/test_config_flow.py | 1 + tests/components/mqtt/test_init.py | 6 ++--- tests/components/mqtt/test_server.py | 6 ++--- tests/components/tradfri/test_init.py | 4 ++++ 20 files changed, 68 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/ffmpeg_motion/manifest.json b/homeassistant/components/ffmpeg_motion/manifest.json index bc5dfaa4209b34..e9a0e7b10143f5 100644 --- a/homeassistant/components/ffmpeg_motion/manifest.json +++ b/homeassistant/components/ffmpeg_motion/manifest.json @@ -3,6 +3,6 @@ "name": "Ffmpeg motion", "documentation": "https://www.home-assistant.io/components/ffmpeg_motion", "requirements": [], - "dependencies": [], + "dependencies": ["ffmpeg"], "codeowners": [] } diff --git a/homeassistant/components/ffmpeg_noise/manifest.json b/homeassistant/components/ffmpeg_noise/manifest.json index 6fdf07899fd7ba..71600b311177f5 100644 --- a/homeassistant/components/ffmpeg_noise/manifest.json +++ b/homeassistant/components/ffmpeg_noise/manifest.json @@ -3,6 +3,6 @@ "name": "Ffmpeg noise", "documentation": "https://www.home-assistant.io/components/ffmpeg_noise", "requirements": [], - "dependencies": [], + "dependencies": ["ffmpeg"], "codeowners": [] } diff --git a/homeassistant/components/html5/manifest.json b/homeassistant/components/html5/manifest.json index 81e4072e62994a..7b43ec44ef3868 100644 --- a/homeassistant/components/html5/manifest.json +++ b/homeassistant/components/html5/manifest.json @@ -5,7 +5,7 @@ "requirements": [ "pywebpush==1.9.2" ], - "dependencies": [], + "dependencies": ["frontend"], "codeowners": [ "@robbiet480" ] diff --git a/homeassistant/components/meraki/manifest.json b/homeassistant/components/meraki/manifest.json index d17e7c60525eab..d03679ed41ed4b 100644 --- a/homeassistant/components/meraki/manifest.json +++ b/homeassistant/components/meraki/manifest.json @@ -3,6 +3,6 @@ "name": "Meraki", "documentation": "https://www.home-assistant.io/components/meraki", "requirements": [], - "dependencies": [], + "dependencies": ["http"], "codeowners": [] } diff --git a/homeassistant/components/microsoft_face_detect/manifest.json b/homeassistant/components/microsoft_face_detect/manifest.json index 955b67a0a76be5..b272a299cf5b93 100644 --- a/homeassistant/components/microsoft_face_detect/manifest.json +++ b/homeassistant/components/microsoft_face_detect/manifest.json @@ -3,6 +3,6 @@ "name": "Microsoft face detect", "documentation": "https://www.home-assistant.io/components/microsoft_face_detect", "requirements": [], - "dependencies": [], + "dependencies": ["microsoft_face"], "codeowners": [] } diff --git a/homeassistant/components/microsoft_face_identify/manifest.json b/homeassistant/components/microsoft_face_identify/manifest.json index f32b9220b3da55..10e4bde103cfc9 100644 --- a/homeassistant/components/microsoft_face_identify/manifest.json +++ b/homeassistant/components/microsoft_face_identify/manifest.json @@ -3,6 +3,6 @@ "name": "Microsoft face identify", "documentation": "https://www.home-assistant.io/components/microsoft_face_identify", "requirements": [], - "dependencies": [], + "dependencies": ["microsoft_face"], "codeowners": [] } diff --git a/homeassistant/components/push/manifest.json b/homeassistant/components/push/manifest.json index 96b9e647e14fee..278638caff8841 100644 --- a/homeassistant/components/push/manifest.json +++ b/homeassistant/components/push/manifest.json @@ -3,7 +3,7 @@ "name": "Push", "documentation": "https://www.home-assistant.io/components/push", "requirements": [], - "dependencies": [], + "dependencies": ["webhook"], "codeowners": [ "@dgomes" ] diff --git a/tests/common.py b/tests/common.py index 9fe5375ad7cc25..e04c8347c095fc 100644 --- a/tests/common.py +++ b/tests/common.py @@ -244,7 +244,7 @@ def async_fire_mqtt_message(hass, topic, payload, qos=0, retain=False): if isinstance(payload, str): payload = payload.encode('utf-8') msg = mqtt.Message(topic, payload, qos, retain) - hass.async_run_job(hass.data['mqtt']._mqtt_on_message, None, None, msg) + hass.data['mqtt']._mqtt_handle_message(msg) fire_mqtt_message = threadsafe_callback_factory(async_fire_mqtt_message) @@ -287,8 +287,7 @@ def mock_state_change_event(hass, new_state, old_state=None): hass.bus.fire(EVENT_STATE_CHANGED, event_data, context=new_state.context) -@asyncio.coroutine -def async_mock_mqtt_component(hass, config=None): +async def async_mock_mqtt_component(hass, config=None): """Mock the MQTT component.""" if config is None: config = {mqtt.CONF_BROKER: 'mock-broker'} @@ -299,10 +298,11 @@ def async_mock_mqtt_component(hass, config=None): mock_client().unsubscribe.return_value = (0, 0) mock_client().publish.return_value = (0, 0) - result = yield from async_setup_component(hass, mqtt.DOMAIN, { + result = await async_setup_component(hass, mqtt.DOMAIN, { mqtt.DOMAIN: config }) assert result + await hass.async_block_till_done() hass.data['mqtt'] = MagicMock(spec_set=hass.data['mqtt'], wraps=hass.data['mqtt']) @@ -708,7 +708,7 @@ def mock_psc(hass, config_input, domain): yield config if domain is None: - assert len(config) == 1, ('assert_setup_component requires DOMAIN: {}' + assert len(config) >= 1, ('assert_setup_component requires DOMAIN: {}' .format(list(config.keys()))) domain = list(config.keys())[0] diff --git a/tests/components/demo/test_geo_location.py b/tests/components/demo/test_geo_location.py index 5a46ca998396c9..c4d01b812f8597 100644 --- a/tests/components/demo/test_geo_location.py +++ b/tests/components/demo/test_geo_location.py @@ -38,10 +38,12 @@ def test_setup_platform(self): with patch('homeassistant.util.dt.utcnow', return_value=utcnow): with assert_setup_component(1, geo_location.DOMAIN): assert setup_component(self.hass, geo_location.DOMAIN, CONFIG) + self.hass.block_till_done() - # In this test, only entities of the geolocation domain have been + # In this test, five geolocation entities have been # generated. all_states = self.hass.states.all() + print(all_states) assert len(all_states) == NUMBER_OF_DEMO_DEVICES # Check a single device's attributes. diff --git a/tests/components/demo/test_notify.py b/tests/components/demo/test_notify.py index 35cf8abe6bd391..964612cb977953 100644 --- a/tests/components/demo/test_notify.py +++ b/tests/components/demo/test_notify.py @@ -45,6 +45,7 @@ def _setup_notify(self): with assert_setup_component(1) as config: assert setup_component(self.hass, notify.DOMAIN, CONFIG) assert config[notify.DOMAIN] + self.hass.block_till_done() def test_setup(self): """Test setup.""" diff --git a/tests/components/ffmpeg/test_sensor.py b/tests/components/ffmpeg/test_sensor.py index d1fd6124b4cb4d..19c497514b7f53 100644 --- a/tests/components/ffmpeg/test_sensor.py +++ b/tests/components/ffmpeg/test_sensor.py @@ -29,6 +29,7 @@ def test_setup_component(self): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_noise') is not None @@ -39,6 +40,7 @@ def test_setup_component_start(self, mock_start): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_noise') is not None @@ -54,6 +56,7 @@ def test_setup_component_start_callback(self, mock_ffmpeg): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_noise') is not None @@ -92,6 +95,7 @@ def test_setup_component(self): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_motion') is not None @@ -102,6 +106,7 @@ def test_setup_component_start(self, mock_start): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_motion') is not None @@ -117,6 +122,7 @@ def test_setup_component_start_callback(self, mock_ffmpeg): """Set up ffmpeg component.""" with assert_setup_component(1, 'binary_sensor'): setup_component(self.hass, 'binary_sensor', self.config) + self.hass.block_till_done() assert self.hass.data['ffmpeg'].binary == 'ffmpeg' assert self.hass.states.get('binary_sensor.ffmpeg_motion') is not None diff --git a/tests/components/geofency/test_init.py b/tests/components/geofency/test_init.py index dd87a6d9503594..98edd8b3af1fbc 100644 --- a/tests/components/geofency/test_init.py +++ b/tests/components/geofency/test_init.py @@ -113,34 +113,34 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -def geofency_client(loop, hass, aiohttp_client): +async def geofency_client(loop, hass, aiohttp_client): """Geofency mock client (unauthenticated).""" - assert loop.run_until_complete(async_setup_component( - hass, 'persistent_notification', {})) + assert await async_setup_component( + hass, 'persistent_notification', {}) - assert loop.run_until_complete(async_setup_component( + assert await async_setup_component( hass, DOMAIN, { DOMAIN: { CONF_MOBILE_BEACONS: ['Car 1'] - }})) - - loop.run_until_complete(hass.async_block_till_done()) + }}) + await hass.async_block_till_done() with patch('homeassistant.components.device_tracker.update_config'): - yield loop.run_until_complete(aiohttp_client(hass.http.app)) + return await aiohttp_client(hass.http.app) @pytest.fixture(autouse=True) -def setup_zones(loop, hass): +async def setup_zones(loop, hass): """Set up Zone config in HA.""" - assert loop.run_until_complete(async_setup_component( + assert await async_setup_component( hass, zone.DOMAIN, { 'zone': { 'name': 'Home', 'latitude': HOME_LATITUDE, 'longitude': HOME_LONGITUDE, 'radius': 100, - }})) + }}) + await hass.async_block_till_done() @pytest.fixture @@ -156,6 +156,7 @@ async def webhook_id(hass, geofency_client): result['flow_id'], {}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() return result['result'].data['webhook_id'] diff --git a/tests/components/gpslogger/test_init.py b/tests/components/gpslogger/test_init.py index 577da5f33e6e99..fce93d0a774bea 100644 --- a/tests/components/gpslogger/test_init.py +++ b/tests/components/gpslogger/test_init.py @@ -26,31 +26,34 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -def gpslogger_client(loop, hass, aiohttp_client): +async def gpslogger_client(loop, hass, aiohttp_client): """Mock client for GPSLogger (unauthenticated).""" - assert loop.run_until_complete(async_setup_component( - hass, 'persistent_notification', {})) + assert await async_setup_component( + hass, 'persistent_notification', {}) - assert loop.run_until_complete(async_setup_component( + assert await async_setup_component( hass, DOMAIN, { DOMAIN: {} - })) + }) + + await hass.async_block_till_done() with patch('homeassistant.components.device_tracker.update_config'): - yield loop.run_until_complete(aiohttp_client(hass.http.app)) + return await aiohttp_client(hass.http.app) @pytest.fixture(autouse=True) -def setup_zones(loop, hass): +async def setup_zones(loop, hass): """Set up Zone config in HA.""" - assert loop.run_until_complete(async_setup_component( + assert await async_setup_component( hass, zone.DOMAIN, { 'zone': { 'name': 'Home', 'latitude': HOME_LATITUDE, 'longitude': HOME_LONGITUDE, 'radius': 100, - }})) + }}) + await hass.async_block_till_done() @pytest.fixture @@ -66,6 +69,7 @@ async def webhook_id(hass, gpslogger_client): result['flow_id'], {}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() return result['result'].data['webhook_id'] diff --git a/tests/components/html5/test_notify.py b/tests/components/html5/test_notify.py index 140544bf9ea068..cae4db6434a0c0 100644 --- a/tests/components/html5/test_notify.py +++ b/tests/components/html5/test_notify.py @@ -61,6 +61,7 @@ async def mock_client(hass, hass_client, registrations=None): 'platform': 'html5' } }) + await hass.async_block_till_done() return await hass_client() diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 0025e9bce666b3..90f2651080c955 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -374,10 +374,10 @@ def _mock_isfile(path): return True return real_isfile(path) - def _mock_open(path): + def _mock_open(path, *args, **kwargs): if path == user_light_file: return StringIO(profile_data) - return real_open(path) + return real_open(path, *args, **kwargs) profile_data = "id,x,y,brightness\n" +\ "group.all_lights.default,.4,.6,99\n" @@ -412,10 +412,10 @@ def _mock_isfile(path): return True return real_isfile(path) - def _mock_open(path): + def _mock_open(path, *args, **kwargs): if path == user_light_file: return StringIO(profile_data) - return real_open(path) + return real_open(path, *args, **kwargs) profile_data = "id,x,y,brightness\n" +\ "group.all_lights.default,.3,.5,200\n" +\ diff --git a/tests/components/locative/test_init.py b/tests/components/locative/test_init.py index f757080eadc72c..6d541cac653923 100644 --- a/tests/components/locative/test_init.py +++ b/tests/components/locative/test_init.py @@ -22,15 +22,16 @@ def mock_dev_track(mock_device_tracker_conf): @pytest.fixture -def locative_client(loop, hass, hass_client): +async def locative_client(loop, hass, hass_client): """Locative mock client.""" - assert loop.run_until_complete(async_setup_component( + assert await async_setup_component( hass, DOMAIN, { DOMAIN: {} - })) + }) + await hass.async_block_till_done() with patch('homeassistant.components.device_tracker.update_config'): - yield loop.run_until_complete(hass_client()) + return await hass_client() @pytest.fixture @@ -45,6 +46,7 @@ async def webhook_id(hass, locative_client): result = await hass.config_entries.flow.async_configure( result['flow_id'], {}) assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() return result['result'].data['webhook_id'] diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index 9d822ba854b369..64196c9febdc5a 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -81,6 +81,7 @@ async def test_manual_config_set(hass, mock_try_connection, """Test we ignore entry if manual config available.""" assert await async_setup_component( hass, 'mqtt', {'mqtt': {'broker': 'bla'}}) + await hass.async_block_till_done() assert len(mock_finish_setup.mock_calls) == 1 mock_try_connection.return_value = True diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 144ee9c43d8017..dc9299e4a359ac 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -28,8 +28,7 @@ def mock_MQTT(): yield mock_MQTT -@asyncio.coroutine -def async_mock_mqtt_client(hass, config=None): +async def async_mock_mqtt_client(hass, config=None): """Mock the MQTT paho client.""" if config is None: config = {mqtt.CONF_BROKER: 'mock-broker'} @@ -39,10 +38,11 @@ def async_mock_mqtt_client(hass, config=None): mock_client().subscribe.return_value = (0, 0) mock_client().unsubscribe.return_value = (0, 0) mock_client().publish.return_value = (0, 0) - result = yield from async_setup_component(hass, mqtt.DOMAIN, { + result = await async_setup_component(hass, mqtt.DOMAIN, { mqtt.DOMAIN: config }) assert result + await hass.async_block_till_done() return mock_client() diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py index 71ef1dc1e4302c..ba05459185d142 100644 --- a/tests/components/mqtt/test_server.py +++ b/tests/components/mqtt/test_server.py @@ -36,9 +36,8 @@ def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt): assert setup_component(self.hass, mqtt.DOMAIN, { mqtt.DOMAIN: {CONF_PASSWORD: password}, }) + self.hass.block_till_done() assert mock_mqtt.called - from pprint import pprint - pprint(mock_mqtt.mock_calls) assert mock_mqtt.mock_calls[1][2]['username'] == 'homeassistant' assert mock_mqtt.mock_calls[1][2]['password'] == password @@ -61,9 +60,8 @@ def test_creating_config_with_pass_and_http_pass(self, mock_mqtt): 'http': {'api_password': 'http_secret'}, mqtt.DOMAIN: {CONF_PASSWORD: password}, }) + self.hass.block_till_done() assert mock_mqtt.called - from pprint import pprint - pprint(mock_mqtt.mock_calls) assert mock_mqtt.mock_calls[1][2]['username'] == 'homeassistant' assert mock_mqtt.mock_calls[1][2]['password'] == password diff --git a/tests/components/tradfri/test_init.py b/tests/components/tradfri/test_init.py index 800c7b72ee6046..4c2ad9d57c9a29 100644 --- a/tests/components/tradfri/test_init.py +++ b/tests/components/tradfri/test_init.py @@ -21,6 +21,7 @@ async def test_config_yaml_host_not_imported(hass): 'host': 'mock-host' } }) + await hass.async_block_till_done() assert len(mock_init.mock_calls) == 0 @@ -34,6 +35,7 @@ async def test_config_yaml_host_imported(hass): 'host': 'mock-host' } }) + await hass.async_block_till_done() progress = hass.config_entries.flow.async_progress() assert len(progress) == 1 @@ -54,6 +56,7 @@ async def test_config_json_host_not_imported(hass): assert await async_setup_component(hass, 'tradfri', { 'tradfri': {} }) + await hass.async_block_till_done() assert len(mock_init.mock_calls) == 0 @@ -65,6 +68,7 @@ async def test_config_json_host_imported(hass, mock_gateway_info): assert await async_setup_component(hass, 'tradfri', { 'tradfri': {} }) + await hass.async_block_till_done() progress = hass.config_entries.flow.async_progress() assert len(progress) == 1 From 43487aa0d68ca8d8f37303a0c5cda1dfaed6a010 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 9 Apr 2019 02:24:51 -0400 Subject: [PATCH 519/605] Stream Timestamp Fixes (#22912) * reset timestamps for streams that do not do so when first requested * update inline comments to be more descriptive --- homeassistant/components/stream/hls.py | 2 +- homeassistant/components/stream/worker.py | 24 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/stream/hls.py b/homeassistant/components/stream/hls.py index c19db4f203f323..467e4751208041 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -85,7 +85,7 @@ def render_playlist(track, start_time): for sequence in segments: segment = track.get_segment(sequence) playlist.extend([ - "#EXTINF:{:.04},".format(float(segment.duration)), + "#EXTINF:{:.04f},".format(float(segment.duration)), "./segment/{}.ts".format(segment.sequence), ]) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 0292fd305964a3..d9bc248dc24064 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -55,10 +55,16 @@ def stream_worker(hass, stream, quit_event): audio_frame = generate_audio_frame() - outputs = {} first_packet = True + # Holds the buffers for each stream provider + outputs = {} + # Keep track of the number of segments we've processed sequence = 1 + # Holds the generated silence that needs to be muxed into the output audio_packets = {} + # The presentation timestamp of the first video packet we recieve + first_pts = 0 + # The decoder timestamp of the latest packet we processed last_dts = None while not quit_event.is_set(): @@ -82,10 +88,18 @@ def stream_worker(hass, stream, quit_event): continue last_dts = packet.dts + # Reset timestamps from a 0 time base for this stream + packet.dts -= first_pts + packet.pts -= first_pts + # Reset segment on every keyframe if packet.is_keyframe: - # Save segment to outputs + # Calculate the segment duration by multiplying the presentation + # timestamp by the time base, which gets us total seconds. + # By then dividing by the seqence, we can calculate how long + # each segment is, assuming the stream starts from 0. segment_duration = (packet.pts * packet.time_base) / sequence + # Save segment to outputs for fmt, buffer in outputs.items(): buffer.output.close() del audio_packets[buffer.astream] @@ -112,6 +126,12 @@ def stream_worker(hass, stream, quit_event): # First video packet tends to have a weird dts/pts if first_packet: + # If we are attaching to a live stream that does not reset + # timestamps for us, we need to do it ourselves by recording + # the first presentation timestamp and subtracting it from + # subsequent packets we recieve. + if (packet.pts * packet.time_base) > 1: + first_pts = packet.pts packet.dts = 0 packet.pts = 0 first_packet = False From 6ee23bdf4e45df812f7ab625dd0fa244bfbd238c Mon Sep 17 00:00:00 2001 From: Ben Dews Date: Tue, 9 Apr 2019 16:31:34 +1000 Subject: [PATCH 520/605] Add Somfy MyLink support for Covers (#22514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added MyLink component * Updated requirements.txt * Fix lint issues * Removed ‘Scene’ functionality * Removed state restoration, as state is no longer tracked * Add component manifest * Remove documentation links in Docstring * Removed redundant try/except block * Removed default dict * Removed features already implemented in default CoverDevice * Removed attributes for tracking state * Simplified loading of covers No options exist other than reversal, so just check reversal status directly and update if needed * Reimplemented is_closed property * Import ENTITY_ID_FORMAT from base component * Removed misc unused vars * Update module docstrings to one line * Removed too many blank lines, giving one back :) * Return none on TimeoutError * Added component to .coveragerc --- .coveragerc | 1 + .../components/somfy_mylink/__init__.py | 63 ++++++++++++++ .../components/somfy_mylink/cover.py | 85 +++++++++++++++++++ .../components/somfy_mylink/manifest.json | 10 +++ requirements_all.txt | 3 + 5 files changed, 162 insertions(+) create mode 100755 homeassistant/components/somfy_mylink/__init__.py create mode 100755 homeassistant/components/somfy_mylink/cover.py create mode 100644 homeassistant/components/somfy_mylink/manifest.json diff --git a/.coveragerc b/.coveragerc index 5eac18b8d7e072..a87b4d7f8f424e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -539,6 +539,7 @@ omit = homeassistant/components/sochain/sensor.py homeassistant/components/socialblade/sensor.py homeassistant/components/solaredge/sensor.py + homeassistant/components/somfy_mylink/* homeassistant/components/sonarr/sensor.py homeassistant/components/songpal/media_player.py homeassistant/components/sonos/* diff --git a/homeassistant/components/somfy_mylink/__init__.py b/homeassistant/components/somfy_mylink/__init__.py new file mode 100755 index 00000000000000..c8a6314acaaa50 --- /dev/null +++ b/homeassistant/components/somfy_mylink/__init__.py @@ -0,0 +1,63 @@ +"""Component for the Somfy MyLink device supporting the Synergy API.""" +import logging + +import voluptuous as vol + +from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.discovery import async_load_platform + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = ['somfy-mylink-synergy==1.0.4'] +CONF_ENTITY_CONFIG = 'entity_config' +CONF_SYSTEM_ID = 'system_id' +CONF_REVERSE = 'reverse' +CONF_DEFAULT_REVERSE = 'default_reverse' +DATA_SOMFY_MYLINK = 'somfy_mylink_data' +DOMAIN = 'somfy_mylink' +SOMFY_MYLINK_COMPONENTS = [ + 'cover' +] + + +def validate_entity_config(values): + """Validate config entry for CONF_ENTITY.""" + entity_config_schema = vol.Schema({ + vol.Optional(CONF_REVERSE): cv.boolean + }) + if not isinstance(values, dict): + raise vol.Invalid('expected a dictionary') + entities = {} + for entity_id, config in values.items(): + entity = cv.entity_id(entity_id) + config = entity_config_schema(config) + entities[entity] = config + return entities + + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_SYSTEM_ID): cv.string, + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_PORT, default=44100): cv.port, + vol.Optional(CONF_DEFAULT_REVERSE, default=False): cv.boolean, + vol.Optional(CONF_ENTITY_CONFIG, default={}): validate_entity_config + }) +}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + """Set up the MyLink platform.""" + from somfy_mylink_synergy import SomfyMyLinkSynergy + host = config[DOMAIN][CONF_HOST] + port = config[DOMAIN][CONF_PORT] + system_id = config[DOMAIN][CONF_SYSTEM_ID] + entity_config = config[DOMAIN][CONF_ENTITY_CONFIG] + entity_config[CONF_DEFAULT_REVERSE] = config[DOMAIN][CONF_DEFAULT_REVERSE] + somfy_mylink = SomfyMyLinkSynergy(system_id, host, port) + hass.data[DATA_SOMFY_MYLINK] = somfy_mylink + for component in SOMFY_MYLINK_COMPONENTS: + hass.async_create_task(async_load_platform( + hass, component, DOMAIN, entity_config, + config)) + return True diff --git a/homeassistant/components/somfy_mylink/cover.py b/homeassistant/components/somfy_mylink/cover.py new file mode 100755 index 00000000000000..c5ea70a600d9ea --- /dev/null +++ b/homeassistant/components/somfy_mylink/cover.py @@ -0,0 +1,85 @@ +"""Cover Platform for the Somfy MyLink component.""" +import logging + +from homeassistant.components.cover import ENTITY_ID_FORMAT, CoverDevice +from homeassistant.util import slugify + +from . import CONF_DEFAULT_REVERSE, DATA_SOMFY_MYLINK + +_LOGGER = logging.getLogger(__name__) +DEPENDENCIES = ['somfy_mylink'] + + +async def async_setup_platform(hass, + config, + async_add_entities, + discovery_info=None): + """Discover and configure Somfy covers.""" + if discovery_info is None: + return + somfy_mylink = hass.data[DATA_SOMFY_MYLINK] + cover_list = [] + try: + mylink_status = await somfy_mylink.status_info() + except TimeoutError: + _LOGGER.error("Unable to connect to the Somfy MyLink device, " + "please check your settings") + return + for cover in mylink_status['result']: + entity_id = ENTITY_ID_FORMAT.format(slugify(cover['name'])) + entity_config = discovery_info.get(entity_id, {}) + default_reverse = discovery_info[CONF_DEFAULT_REVERSE] + cover_config = {} + cover_config['target_id'] = cover['targetID'] + cover_config['name'] = cover['name'] + cover_config['reverse'] = entity_config.get('reverse', default_reverse) + cover_list.append(SomfyShade(somfy_mylink, **cover_config)) + _LOGGER.info('Adding Somfy Cover: %s with targetID %s', + cover_config['name'], cover_config['target_id']) + async_add_entities(cover_list) + + +class SomfyShade(CoverDevice): + """Object for controlling a Somfy cover.""" + + def __init__(self, somfy_mylink, target_id='AABBCC', name='SomfyShade', + reverse=False, device_class='window'): + """Initialize the cover.""" + self.somfy_mylink = somfy_mylink + self._target_id = target_id + self._name = name + self._reverse = reverse + self._device_class = device_class + + @property + def name(self): + """Return the name of the cover.""" + return self._name + + @property + def is_closed(self): + """Return if the cover is closed.""" + pass + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._device_class + + async def async_open_cover(self, **kwargs): + """Wrap Homeassistant calls to open the cover.""" + if not self._reverse: + await self.somfy_mylink.move_up(self._target_id) + else: + await self.somfy_mylink.move_down(self._target_id) + + async def async_close_cover(self, **kwargs): + """Wrap Homeassistant calls to close the cover.""" + if not self._reverse: + await self.somfy_mylink.move_down(self._target_id) + else: + await self.somfy_mylink.move_up(self._target_id) + + async def async_stop_cover(self, **kwargs): + """Stop the cover.""" + await self.somfy_mylink.move_stop(self._target_id) diff --git a/homeassistant/components/somfy_mylink/manifest.json b/homeassistant/components/somfy_mylink/manifest.json new file mode 100644 index 00000000000000..5a3cec0def8bc2 --- /dev/null +++ b/homeassistant/components/somfy_mylink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "somfy_mylink", + "name": "Somfy MyLink", + "documentation": "https://www.home-assistant.io/components/somfy_mylink", + "requirements": [ + "somfy-mylink-synergy==1.0.4" + ], + "dependencies": [], + "codeowners": [] + } \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index b64079b4460d98..26668571462d9e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1598,6 +1598,9 @@ solaredge==0.0.2 # homeassistant.components.honeywell somecomfort==0.5.2 +# homeassistant.components.somfy_mylink +somfy-mylink-synergy==1.0.4 + # homeassistant.components.speedtestdotnet speedtest-cli==2.1.1 From 75bed93d3d67be7449a47dc134a7ebdc75a57c55 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 9 Apr 2019 02:47:57 -0700 Subject: [PATCH 521/605] Add cloudhook and remote UI vals to get_config (#22921) --- .../components/mobile_app/webhook.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 28ef6bccd6add2..2a3d23731cb91e 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -4,6 +4,8 @@ from aiohttp.web import HTTPBadRequest, Response, Request import voluptuous as vol +from homeassistant.components.cloud import (async_remote_ui_url, + CloudNotAvailable) from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES, ATTR_DEV_ID, DOMAIN as DT_DOMAIN, @@ -31,14 +33,15 @@ ATTR_TEMPLATE_VARIABLES, ATTR_VERTICAL_ACCURACY, ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, - CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, - DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, - ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, - SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_CONFIG, - WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + CONF_CLOUDHOOK_URL, CONF_REMOTE_UI_URL, CONF_SECRET, + DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_STORE, DOMAIN, + ERR_ENCRYPTION_REQUIRED, ERR_SENSOR_DUPLICATE_UNIQUE_ID, + ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, + WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPES, + WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, + WEBHOOK_TYPE_GET_CONFIG, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -286,7 +289,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, hass_config = hass.config.as_dict() - return webhook_response({ + resp = { 'latitude': hass_config['latitude'], 'longitude': hass_config['longitude'], 'elevation': hass_config['elevation'], @@ -296,4 +299,15 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, 'components': hass_config['components'], 'version': hass_config['version'], 'theme_color': MANIFEST_JSON['theme_color'], - }, registration=registration, headers=headers) + } + + if CONF_CLOUDHOOK_URL in registration: + resp[CONF_CLOUDHOOK_URL] = registration[CONF_CLOUDHOOK_URL] + + try: + resp[CONF_REMOTE_UI_URL] = async_remote_ui_url(hass) + except CloudNotAvailable: + pass + + return webhook_response(resp, registration=registration, + headers=headers) From 64ea13104e6c6e248871e47ac4136360ec032698 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Tue, 9 Apr 2019 05:48:17 -0400 Subject: [PATCH 522/605] Fix ZHA Light color conversion. (#22909) --- homeassistant/components/zha/core/channels/lighting.py | 1 + homeassistant/components/zha/light.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index c7cbdc67db9789..0a96b4db4b85a1 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -29,6 +29,7 @@ def get_color_capabilities(self): async def async_configure(self): """Configure channel.""" await self.fetch_color_capabilities(False) + await super().async_configure() async def async_initialize(self, from_cache): """Initialize channel.""" diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index eadc9e03af0321..12bc12c5f6edb1 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -266,7 +266,9 @@ async def async_get_state(self, from_cache=True): 'current_x', from_cache=from_cache) color_y = await self._color_channel.get_attribute_value( 'current_y', from_cache=from_cache) - self._hs_color = color_util.color_xy_to_hs(color_x, color_y) + if color_x is not None and color_y is not None: + self._hs_color = color_util.color_xy_to_hs( + float(color_x / 65535), float(color_y / 65535)) async def refresh(self, time): """Call async_get_state at an interval.""" From fd8d9747ef5ebae6e59b3c13fca72b300eaa5a07 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 9 Apr 2019 02:48:59 -0700 Subject: [PATCH 523/605] More Mobile app sensor fixes (#22914) * Ensure we only add a sensor once * Ensure that we dont process updates for entities that arent what we were setup for * Add debug logging to ease development of apps * Use str representation --- .../components/mobile_app/binary_sensor.py | 13 ++++++++++++- homeassistant/components/mobile_app/entity.py | 14 ++++++++++++-- homeassistant/components/mobile_app/sensor.py | 15 +++++++++++++-- homeassistant/components/mobile_app/webhook.py | 3 +++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 289a50584c92d3..50943bb6504d8b 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -8,9 +8,10 @@ from .const import (ATTR_SENSOR_STATE, ATTR_SENSOR_TYPE_BINARY_SENSOR as ENTITY_TYPE, + ATTR_SENSOR_UNIQUE_ID, DATA_DEVICES, DOMAIN) -from .entity import MobileAppEntity +from .entity import MobileAppEntity, sensor_id DEPENDENCIES = ['mobile_app'] @@ -36,6 +37,16 @@ def handle_sensor_registration(webhook_id, data): if data[CONF_WEBHOOK_ID] != webhook_id: return + unique_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + + entity = hass.data[DOMAIN][ENTITY_TYPE][unique_id] + + if 'added' in entity: + return + + entity['added'] = True + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] async_add_entities([MobileAppBinarySensor(data, device, config_entry)]) diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index 05736b3a689b37..eca9d2b024bd40 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -13,6 +13,11 @@ DOMAIN, SIGNAL_SENSOR_UPDATE) +def sensor_id(webhook_id, unique_id): + """Return a unique sensor ID.""" + return "{}_{}".format(webhook_id, unique_id) + + class MobileAppEntity(Entity): """Representation of an mobile app entity.""" @@ -22,8 +27,8 @@ def __init__(self, config: dict, device: DeviceEntry, entry: ConfigEntry): self._device = device self._entry = entry self._registration = entry.data - self._sensor_id = "{}_{}".format(self._registration[CONF_WEBHOOK_ID], - config[ATTR_SENSOR_UNIQUE_ID]) + self._sensor_id = sensor_id(self._registration[CONF_WEBHOOK_ID], + config[ATTR_SENSOR_UNIQUE_ID]) self._entity_type = config[ATTR_SENSOR_TYPE] self.unsub_dispatcher = None @@ -94,5 +99,10 @@ async def async_update(self): @callback def _handle_update(self, data): """Handle async event updates.""" + incoming_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + if incoming_id != self._sensor_id: + return + self._config = data self.async_schedule_update_ha_state() diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index b2846a6002b17f..64ad69c5758ecc 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -7,9 +7,10 @@ from .const import (ATTR_SENSOR_STATE, ATTR_SENSOR_TYPE_SENSOR as ENTITY_TYPE, - ATTR_SENSOR_UOM, DATA_DEVICES, DOMAIN) + ATTR_SENSOR_UNIQUE_ID, ATTR_SENSOR_UOM, DATA_DEVICES, + DOMAIN) -from .entity import MobileAppEntity +from .entity import MobileAppEntity, sensor_id DEPENDENCIES = ['mobile_app'] @@ -35,6 +36,16 @@ def handle_sensor_registration(webhook_id, data): if data[CONF_WEBHOOK_ID] != webhook_id: return + unique_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + + entity = hass.data[DOMAIN][ENTITY_TYPE][unique_id] + + if 'added' in entity: + return + + entity['added'] = True + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] async_add_entities([MobileAppSensor(data, device, config_entry)]) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 2a3d23731cb91e..1ef5f4ce531764 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -99,6 +99,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, data = webhook_payload + _LOGGER.debug("Received webhook payload for type %s: %s", webhook_type, + data) + if webhook_type in WEBHOOK_SCHEMAS: try: data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) From d48fe4cebc8bdfc9e5609796f38c0f7c4e08a0da Mon Sep 17 00:00:00 2001 From: John Raahauge <43510812+AZDane@users.noreply.github.com> Date: Tue, 9 Apr 2019 02:49:48 -0700 Subject: [PATCH 524/605] Added features to Concord232 Alarm Panel (#22892) * Added features to Concord232 Alarm Panel * Update homeassistant/components/concord232/alarm_control_panel.py Deleted 'or None' as per @syssi suggestion. --- .../concord232/alarm_control_panel.py | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/concord232/alarm_control_panel.py b/homeassistant/components/concord232/alarm_control_panel.py index 4821e589b13ed1..a209fba93edfb3 100644 --- a/homeassistant/components/concord232/alarm_control_panel.py +++ b/homeassistant/components/concord232/alarm_control_panel.py @@ -9,8 +9,8 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_HOST, CONF_NAME, CONF_PORT, STATE_ALARM_ARMED_AWAY, - STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) + CONF_HOST, CONF_NAME, CONF_PORT, CONF_CODE, CONF_MODE, + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) REQUIREMENTS = ['concord232==0.15'] @@ -19,12 +19,15 @@ DEFAULT_HOST = 'localhost' DEFAULT_NAME = 'CONCORD232' DEFAULT_PORT = 5007 +DEFAULT_MODE = 'audible' SCAN_INTERVAL = datetime.timedelta(seconds=10) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_CODE): cv.string, + vol.Optional(CONF_MODE, default=DEFAULT_MODE): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, }) @@ -32,13 +35,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Concord232 alarm control panel platform.""" name = config.get(CONF_NAME) + code = config.get(CONF_CODE) + mode = config.get(CONF_MODE) host = config.get(CONF_HOST) port = config.get(CONF_PORT) url = 'http://{}:{}'.format(host, port) try: - add_entities([Concord232Alarm(url, name)], True) + add_entities([Concord232Alarm(url, name, code, mode)], True) except requests.exceptions.ConnectionError as ex: _LOGGER.error("Unable to connect to Concord232: %s", str(ex)) @@ -46,12 +51,14 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class Concord232Alarm(alarm.AlarmControlPanel): """Representation of the Concord232-based alarm panel.""" - def __init__(self, url, name): + def __init__(self, url, name, code, mode): """Initialize the Concord232 alarm panel.""" from concord232 import client as concord232_client self._state = None self._name = name + self._code = code + self._mode = mode self._url = url self._alarm = concord232_client.Client(self._url) self._alarm.partitions = self._alarm.list_partitions() @@ -93,12 +100,35 @@ def update(self): def alarm_disarm(self, code=None): """Send disarm command.""" + if not self._validate_code(code, STATE_ALARM_DISARMED): + return self._alarm.disarm(code) def alarm_arm_home(self, code=None): """Send arm home command.""" - self._alarm.arm('stay') + if not self._validate_code(code, STATE_ALARM_ARMED_HOME): + return + if self._mode == 'silent': + self._alarm.arm('stay', 'silent') + else: + self._alarm.arm('stay') def alarm_arm_away(self, code=None): """Send arm away command.""" + if not self._validate_code(code, STATE_ALARM_ARMED_AWAY): + return self._alarm.arm('away') + + def _validate_code(self, code, state): + """Validate given code.""" + if self._code is None: + return True + if isinstance(self._code, str): + alarm_code = self._code + else: + alarm_code = self._code.render(from_state=self._state, + to_state=state) + check = not alarm_code or code == alarm_code + if not check: + _LOGGER.warning("Invalid code given for %s", state) + return check From 88694c978b50c1e2dbb2e2a3cd2a0c11909d3685 Mon Sep 17 00:00:00 2001 From: Paul Madden Date: Tue, 9 Apr 2019 03:56:04 -0600 Subject: [PATCH 525/605] Camera component for BOM integration (#22816) * Work on PR comments * Work on PR comments * Update imports * Work on schema validation * Fix package * Add bomradarcam to .coveragerc * Improve error message for location * Delinting * Correct module name in .coveragerc * Add manifest.json * Update requirements_all.txt * Merge bomradarcam into existing bom integration --- .coveragerc | 1 + homeassistant/components/bom/camera.py | 80 ++++++++++++++++++++++ homeassistant/components/bom/manifest.json | 4 +- requirements_all.txt | 3 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/bom/camera.py diff --git a/.coveragerc b/.coveragerc index a87b4d7f8f424e..9f2a31f3d147eb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -64,6 +64,7 @@ omit = homeassistant/components/bme280/sensor.py homeassistant/components/bme680/sensor.py homeassistant/components/bmw_connected_drive/* + homeassistant/components/bom/camera.py homeassistant/components/bom/sensor.py homeassistant/components/bom/weather.py homeassistant/components/braviatv/media_player.py diff --git a/homeassistant/components/bom/camera.py b/homeassistant/components/bom/camera.py new file mode 100644 index 00000000000000..d3e78034015bc1 --- /dev/null +++ b/homeassistant/components/bom/camera.py @@ -0,0 +1,80 @@ +"""Provide animated GIF loops of BOM radar imagery.""" +import voluptuous as vol + +from homeassistant.components.camera import PLATFORM_SCHEMA, Camera +from homeassistant.const import CONF_ID, CONF_NAME +from homeassistant.helpers import config_validation as cv + +REQUIREMENTS = ['bomradarloop==0.1.2'] + +CONF_DELTA = 'delta' +CONF_FRAMES = 'frames' +CONF_LOCATION = 'location' +CONF_OUTFILE = 'filename' + +LOCATIONS = [ + 'Adelaide', 'Albany', 'AliceSprings', 'Bairnsdale', 'Bowen', 'Brisbane', + 'Broome', 'Cairns', 'Canberra', 'Carnarvon', 'Ceduna', 'Dampier', 'Darwin', + 'Emerald', 'Esperance', 'Geraldton', 'Giles', 'Gladstone', 'Gove', + 'Grafton', 'Gympie', 'HallsCreek', 'Hobart', 'Kalgoorlie', 'Katherine', + 'Learmonth', 'Longreach', 'Mackay', 'Marburg', 'Melbourne', 'Mildura', + 'Moree', 'MorningtonIs', 'MountIsa', 'MtGambier', 'Namoi', 'Newcastle', + 'Newdegate', 'NorfolkIs', 'NWTasmania', 'Perth', 'PortHedland', + 'SellicksHill', 'SouthDoodlakine', 'Sydney', 'Townsville', 'WaggaWagga', + 'Warrego', 'Warruwi', 'Watheroo', 'Weipa', 'WillisIs', 'Wollongong', + 'Woomera', 'Wyndham', 'Yarrawonga', +] + + +def _validate_schema(config): + if config.get(CONF_LOCATION) is None: + if not all(config.get(x) for x in (CONF_ID, CONF_DELTA, CONF_FRAMES)): + raise vol.Invalid( + "Specify '{}', '{}' and '{}' when '{}' is unspecified".format( + CONF_ID, CONF_DELTA, CONF_FRAMES, CONF_LOCATION)) + return config + + +LOCATIONS_MSG = "Set '{}' to one of: {}".format( + CONF_LOCATION, ', '.join(sorted(LOCATIONS))) +XOR_MSG = "Specify exactly one of '{}' or '{}'".format(CONF_ID, CONF_LOCATION) + +PLATFORM_SCHEMA = vol.All( + PLATFORM_SCHEMA.extend({ + vol.Exclusive(CONF_ID, 'xor', msg=XOR_MSG): cv.string, + vol.Exclusive(CONF_LOCATION, 'xor', msg=XOR_MSG): vol.In( + LOCATIONS, msg=LOCATIONS_MSG), + vol.Optional(CONF_DELTA): cv.positive_int, + vol.Optional(CONF_FRAMES): cv.positive_int, + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_OUTFILE): cv.string, + }), _validate_schema) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up BOM radar-loop camera component.""" + location = config.get(CONF_LOCATION) or "ID {}".format(config.get(CONF_ID)) + name = config.get(CONF_NAME) or "BOM Radar Loop - {}".format(location) + args = [config.get(x) for x in (CONF_LOCATION, CONF_ID, CONF_DELTA, + CONF_FRAMES, CONF_OUTFILE)] + add_entities([BOMRadarCam(name, *args)]) + + +class BOMRadarCam(Camera): + """A camera component producing animated BOM radar-imagery GIFs.""" + + def __init__(self, name, location, radar_id, delta, frames, outfile): + """Initialize the component.""" + from bomradarloop import BOMRadarLoop + super().__init__() + self._name = name + self._cam = BOMRadarLoop(location, radar_id, delta, frames, outfile) + + def camera_image(self): + """Return the current BOM radar-loop image.""" + return self._cam.current + + @property + def name(self): + """Return the component name.""" + return self._name diff --git a/homeassistant/components/bom/manifest.json b/homeassistant/components/bom/manifest.json index e4744d4cfd2a11..cb7ce4383b0575 100644 --- a/homeassistant/components/bom/manifest.json +++ b/homeassistant/components/bom/manifest.json @@ -2,7 +2,9 @@ "domain": "bom", "name": "Bom", "documentation": "https://www.home-assistant.io/components/bom", - "requirements": [], + "requirements": [ + "bomradarloop==0.1.2" + ], "dependencies": [], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index 26668571462d9e..4d2cfcf8034c09 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -235,6 +235,9 @@ blockchain==1.4.4 # homeassistant.components.bme680 # bme680==1.0.5 +# homeassistant.components.bom +bomradarloop==0.1.2 + # homeassistant.components.amazon_polly # homeassistant.components.aws_lambda # homeassistant.components.aws_sns From f81ce0b720d9023e518785c46cd3d79d52a15645 Mon Sep 17 00:00:00 2001 From: Ben Dews Date: Tue, 9 Apr 2019 19:58:09 +1000 Subject: [PATCH 526/605] Add 'Assumed State' property to Somfy MyLink covers (#22922) * Explicitly return none for is_closed property * Set the assumed_state property to true * Added period to docstring --- homeassistant/components/somfy_mylink/cover.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/somfy_mylink/cover.py b/homeassistant/components/somfy_mylink/cover.py index c5ea70a600d9ea..e0b9eae36ebe73 100755 --- a/homeassistant/components/somfy_mylink/cover.py +++ b/homeassistant/components/somfy_mylink/cover.py @@ -59,7 +59,12 @@ def name(self): @property def is_closed(self): """Return if the cover is closed.""" - pass + return None + + @property + def assumed_state(self): + """Let HA know the integration is assumed state.""" + return True @property def device_class(self): From a48c0f2991bd5848329dc0c190e2b74f71d1ec85 Mon Sep 17 00:00:00 2001 From: Evan Bruhn Date: Tue, 9 Apr 2019 22:26:58 +1000 Subject: [PATCH 527/605] Logi Circle public API refactor and config flow (#20624) * Logi Circle now uses OAuth2 for authentication, added config flow. * Service calls now dispatched to camera entities via signalled events * Update from PR review * Add unit tests for config flow * Updated CODEOWNERS * Reverted change to .coveragerc * Improved test coverage of config flow --- .coveragerc | 5 +- CODEOWNERS | 1 + homeassistant/components/camera/services.yaml | 36 --- .../logi_circle/.translations/en.json | 32 +++ .../components/logi_circle/__init__.py | 208 ++++++++++++++---- .../components/logi_circle/camera.py | 161 ++++++-------- .../components/logi_circle/config_flow.py | 205 +++++++++++++++++ homeassistant/components/logi_circle/const.py | 43 ++++ .../components/logi_circle/manifest.json | 10 +- .../components/logi_circle/sensor.py | 55 ++--- .../components/logi_circle/services.yaml | 37 ++++ .../components/logi_circle/strings.json | 32 +++ homeassistant/config_entries.py | 1 + requirements_all.txt | 2 +- tests/components/logi_circle/__init__.py | 1 + .../logi_circle/test_config_flow.py | 201 +++++++++++++++++ 16 files changed, 821 insertions(+), 209 deletions(-) create mode 100644 homeassistant/components/logi_circle/.translations/en.json create mode 100644 homeassistant/components/logi_circle/config_flow.py create mode 100644 homeassistant/components/logi_circle/const.py create mode 100644 homeassistant/components/logi_circle/services.yaml create mode 100644 homeassistant/components/logi_circle/strings.json create mode 100644 tests/components/logi_circle/__init__.py create mode 100644 tests/components/logi_circle/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 9f2a31f3d147eb..eaf00c7e6ece2a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -321,7 +321,10 @@ omit = homeassistant/components/liveboxplaytv/media_player.py homeassistant/components/llamalab_automate/notify.py homeassistant/components/lockitron/lock.py - homeassistant/components/logi_circle/* + homeassistant/components/logi_circle/__init__.py + homeassistant/components/logi_circle/camera.py + homeassistant/components/logi_circle/const.py + homeassistant/components/logi_circle/sensor.py homeassistant/components/london_underground/sensor.py homeassistant/components/loopenergy/sensor.py homeassistant/components/luci/device_tracker.py diff --git a/CODEOWNERS b/CODEOWNERS index 30a91a7c76d2c3..033f8331ebf788 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -125,6 +125,7 @@ homeassistant/components/lifx_legacy/* @amelchio homeassistant/components/linux_battery/* @fabaff homeassistant/components/liveboxplaytv/* @pschmitt homeassistant/components/logger/* @home-assistant/core +homeassistant/components/logi_circle/* @evanjd homeassistant/components/lovelace/* @home-assistant/core homeassistant/components/luci/* @fbradyirl homeassistant/components/luftdaten/* @fabaff diff --git a/homeassistant/components/camera/services.yaml b/homeassistant/components/camera/services.yaml index a3e42300cbd9c1..4c2d89db86d2dd 100644 --- a/homeassistant/components/camera/services.yaml +++ b/homeassistant/components/camera/services.yaml @@ -93,39 +93,3 @@ onvif_ptz: zoom: description: "Zoom. Allowed values: ZOOM_IN, ZOOM_OUT" example: "ZOOM_IN" - -logi_circle_set_config: - description: Set a configuration property. - fields: - entity_id: - description: Name(s) of entities to apply the operation mode to. - example: "camera.living_room_camera" - mode: - description: "Operation mode. Allowed values: BATTERY_SAVING, LED, PRIVACY_MODE." - example: "PRIVACY_MODE" - value: - description: "Operation value. Allowed values: true, false" - example: true - -logi_circle_livestream_snapshot: - description: Take a snapshot from the camera's livestream. Will wake the camera from sleep if required. - fields: - entity_id: - description: Name(s) of entities to create snapshots from. - example: "camera.living_room_camera" - filename: - description: Template of a Filename. Variable is entity_id. - example: "/tmp/snapshot_{{ entity_id }}.jpg" - -logi_circle_livestream_record: - description: Take a video recording from the camera's livestream. - fields: - entity_id: - description: Name(s) of entities to create recordings from. - example: "camera.living_room_camera" - filename: - description: Template of a Filename. Variable is entity_id. - example: "/tmp/snapshot_{{ entity_id }}.mp4" - duration: - description: Recording duration in seconds. - example: 60 diff --git a/homeassistant/components/logi_circle/.translations/en.json b/homeassistant/components/logi_circle/.translations/en.json new file mode 100644 index 00000000000000..57dd0b709b7102 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/en.json @@ -0,0 +1,32 @@ +{ + "config": { + "title": "Logi Circle", + "step": { + "user": { + "title": "Authentication Provider", + "description": "Pick via which authentication provider you want to authenticate with Logi Circle.", + "data": { + "flow_impl": "Provider" + } + }, + "auth": { + "title": "Authenticate with Logi Circle", + "description": "Please follow the link below and Accept access to your Logi Circle account, then come back and press Submit below.\n\n[Link]({authorization_url})" + } + }, + "create_entry": { + "default": "Successfully authenticated with Logi Circle." + }, + "error": { + "auth_error": "API authorization failed.", + "auth_timeout": "Authorization timed out when requesting access token.", + "follow_link": "Please follow the link and authenticate before pressing Submit." + }, + "abort": { + "already_setup": "You can only configure a single Logi Circle account.", + "external_error": "Exception occurred from another flow.", + "external_setup": "Logi Circle successfully configured from another flow.", + "no_flows": "You need to configure Logi Circle before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/logi_circle/)." + } + } +} diff --git a/homeassistant/components/logi_circle/__init__.py b/homeassistant/components/logi_circle/__init__.py index ef006ef8b4db11..433895293f441d 100644 --- a/homeassistant/components/logi_circle/__init__.py +++ b/homeassistant/components/logi_circle/__init__.py @@ -5,71 +5,203 @@ import async_timeout import voluptuous as vol -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -import homeassistant.helpers.config_validation as cv +from homeassistant import config_entries +from homeassistant.components.camera import ( + ATTR_FILENAME, CAMERA_SERVICE_SCHEMA) +from homeassistant.const import ( + CONF_MONITORED_CONDITIONS, CONF_SENSORS, EVENT_HOMEASSISTANT_STOP) +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['logi_circle==0.1.7'] +from . import config_flow +from .const import ( + CONF_API_KEY, CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_REDIRECT_URI, + DATA_LOGI, DEFAULT_CACHEDB, DOMAIN, LED_MODE_KEY, LOGI_SENSORS, + RECORDING_MODE_KEY, SIGNAL_LOGI_CIRCLE_RECONFIGURE, + SIGNAL_LOGI_CIRCLE_RECORD, SIGNAL_LOGI_CIRCLE_SNAPSHOT) + +REQUIREMENTS = ['logi_circle==0.2.2'] + +NOTIFICATION_ID = 'logi_circle_notification' +NOTIFICATION_TITLE = 'Logi Circle Setup' _LOGGER = logging.getLogger(__name__) _TIMEOUT = 15 # seconds -ATTRIBUTION = "Data provided by circle.logi.com" +SERVICE_SET_CONFIG = 'set_config' +SERVICE_LIVESTREAM_SNAPSHOT = 'livestream_snapshot' +SERVICE_LIVESTREAM_RECORD = 'livestream_record' -NOTIFICATION_ID = 'logi_notification' -NOTIFICATION_TITLE = 'Logi Circle Setup' +ATTR_MODE = 'mode' +ATTR_VALUE = 'value' +ATTR_DURATION = 'duration' + +SENSOR_SCHEMA = vol.Schema({ + vol.Optional(CONF_MONITORED_CONDITIONS, default=list(LOGI_SENSORS)): + vol.All(cv.ensure_list, [vol.In(LOGI_SENSORS)]) +}) + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: + vol.Schema({ + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_REDIRECT_URI): cv.string, + vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA + }) + }, + extra=vol.ALLOW_EXTRA, +) -DOMAIN = 'logi_circle' -DEFAULT_CACHEDB = '.logi_cache.pickle' -DEFAULT_ENTITY_NAMESPACE = 'logi_circle' +LOGI_CIRCLE_SERVICE_SET_CONFIG = CAMERA_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_MODE): vol.In([LED_MODE_KEY, + RECORDING_MODE_KEY]), + vol.Required(ATTR_VALUE): cv.boolean +}) -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - }), -}, extra=vol.ALLOW_EXTRA) +LOGI_CIRCLE_SERVICE_SNAPSHOT = CAMERA_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_FILENAME): cv.template +}) + +LOGI_CIRCLE_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_FILENAME): cv.template, + vol.Required(ATTR_DURATION): cv.positive_int +}) async def async_setup(hass, config): - """Set up the Logi Circle component.""" + """Set up configured Logi Circle component.""" + if DOMAIN not in config: + return True + conf = config[DOMAIN] - username = conf[CONF_USERNAME] - password = conf[CONF_PASSWORD] - try: - from logi_circle import Logi - from logi_circle.exception import BadLogin - from aiohttp.client_exceptions import ClientResponseError + config_flow.register_flow_implementation( + hass, + DOMAIN, + client_id=conf[CONF_CLIENT_ID], + client_secret=conf[CONF_CLIENT_SECRET], + api_key=conf[CONF_API_KEY], + redirect_uri=conf[CONF_REDIRECT_URI], + sensors=conf[CONF_SENSORS]) - cache = hass.config.path(DEFAULT_CACHEDB) - logi = Logi(username=username, password=password, cache_file=cache) + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={'source': config_entries.SOURCE_IMPORT}, + )) - with async_timeout.timeout(_TIMEOUT, loop=hass.loop): - await logi.login() - hass.data[DOMAIN] = await logi.cameras + return True - if not logi.is_connected: - return False - except (BadLogin, ClientResponseError) as ex: - _LOGGER.error('Unable to connect to Logi Circle API: %s', str(ex)) + +async def async_setup_entry(hass, entry): + """Set up Logi Circle from a config entry.""" + from logi_circle import LogiCircle + from logi_circle.exception import AuthorizationFailed + from aiohttp.client_exceptions import ClientResponseError + + logi_circle = LogiCircle( + client_id=entry.data[CONF_CLIENT_ID], + client_secret=entry.data[CONF_CLIENT_SECRET], + api_key=entry.data[CONF_API_KEY], + redirect_uri=entry.data[CONF_REDIRECT_URI], + cache_file=DEFAULT_CACHEDB + ) + + if not logi_circle.authorized: hass.components.persistent_notification.create( - 'Error: {}
' - 'You will need to restart hass after fixing.' - ''.format(ex), + "Error: The cached access tokens are missing from {}.
" + "Please unload then re-add the Logi Circle integration to resolve." + ''.format(DEFAULT_CACHEDB), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + return False + + try: + with async_timeout.timeout(_TIMEOUT, loop=hass.loop): + # Ensure the cameras property returns the same Camera objects for + # all devices. Performs implicit login and session validation. + await logi_circle.synchronize_cameras() + except AuthorizationFailed: + hass.components.persistent_notification.create( + "Error: Failed to obtain an access token from the cached " + "refresh token.
" + "Token may have expired or been revoked.
" + "Please unload then re-add the Logi Circle integration to resolve", title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False except asyncio.TimeoutError: # The TimeoutError exception object returns nothing when casted to a # string, so we'll handle it separately. - err = '{}s timeout exceeded when connecting to Logi Circle API'.format( + err = "{}s timeout exceeded when connecting to Logi Circle API".format( _TIMEOUT) - _LOGGER.error(err) hass.components.persistent_notification.create( - 'Error: {}
' - 'You will need to restart hass after fixing.' + "Error: {}
" + "You will need to restart hass after fixing." ''.format(err), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False + except ClientResponseError as ex: + hass.components.persistent_notification.create( + "Error: {}
" + "You will need to restart hass after fixing." + ''.format(ex), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + return False + + hass.data[DATA_LOGI] = logi_circle + + for component in 'camera', 'sensor': + hass.async_create_task(hass.config_entries.async_forward_entry_setup( + entry, component)) + + async def service_handler(service): + """Dispatch service calls to target entities.""" + params = dict(service.data) + + if service.service == SERVICE_SET_CONFIG: + async_dispatcher_send(hass, SIGNAL_LOGI_CIRCLE_RECONFIGURE, params) + if service.service == SERVICE_LIVESTREAM_SNAPSHOT: + async_dispatcher_send(hass, SIGNAL_LOGI_CIRCLE_SNAPSHOT, params) + if service.service == SERVICE_LIVESTREAM_RECORD: + async_dispatcher_send(hass, SIGNAL_LOGI_CIRCLE_RECORD, params) + + hass.services.async_register( + DOMAIN, SERVICE_SET_CONFIG, service_handler, + schema=LOGI_CIRCLE_SERVICE_SET_CONFIG) + + hass.services.async_register( + DOMAIN, SERVICE_LIVESTREAM_SNAPSHOT, service_handler, + schema=LOGI_CIRCLE_SERVICE_SNAPSHOT) + + hass.services.async_register( + DOMAIN, SERVICE_LIVESTREAM_RECORD, service_handler, + schema=LOGI_CIRCLE_SERVICE_RECORD) + + async def shut_down(event=None): + """Close Logi Circle aiohttp session.""" + await logi_circle.auth_provider.close() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shut_down) + + return True + + +async def async_unload_entry(hass, entry): + """Unload a config entry.""" + for component in 'camera', 'sensor': + await hass.config_entries.async_forward_entry_unload( + entry, component) + + logi_circle = hass.data.pop(DATA_LOGI) + + # Tell API wrapper to close all aiohttp sessions, invalidate WS connections + # and clear all locally cached tokens + await logi_circle.auth_provider.clear_authorization() + return True diff --git a/homeassistant/components/logi_circle/camera.py b/homeassistant/components/logi_circle/camera.py index ff6f14431d5819..b69f23ac19dc7e 100644 --- a/homeassistant/components/logi_circle/camera.py +++ b/homeassistant/components/logi_circle/camera.py @@ -1,114 +1,89 @@ """Support to the Logi Circle cameras.""" -import asyncio from datetime import timedelta import logging -import voluptuous as vol - from homeassistant.components.camera import ( - ATTR_ENTITY_ID, ATTR_FILENAME, CAMERA_SERVICE_SCHEMA, - PLATFORM_SCHEMA, SUPPORT_ON_OFF, Camera) -from homeassistant.components.camera.const import DOMAIN + ATTR_ENTITY_ID, SUPPORT_ON_OFF, Camera) +from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ( - ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, - CONF_SCAN_INTERVAL, STATE_OFF, STATE_ON) -from homeassistant.helpers import config_validation as cv + ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, STATE_OFF, + STATE_ON) +from homeassistant.helpers.dispatcher import async_dispatcher_connect -from . import ATTRIBUTION, DOMAIN as LOGI_CIRCLE_DOMAIN +from .const import ( + ATTRIBUTION, DOMAIN as LOGI_CIRCLE_DOMAIN, LED_MODE_KEY, + RECORDING_MODE_KEY, SIGNAL_LOGI_CIRCLE_RECONFIGURE, + SIGNAL_LOGI_CIRCLE_RECORD, SIGNAL_LOGI_CIRCLE_SNAPSHOT) -DEPENDENCIES = ['logi_circle'] +DEPENDENCIES = ['logi_circle', 'ffmpeg'] _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=60) -SERVICE_SET_CONFIG = 'logi_circle_set_config' -SERVICE_LIVESTREAM_SNAPSHOT = 'logi_circle_livestream_snapshot' -SERVICE_LIVESTREAM_RECORD = 'logi_circle_livestream_record' -DATA_KEY = 'camera.logi_circle' - -BATTERY_SAVING_MODE_KEY = 'BATTERY_SAVING' -PRIVACY_MODE_KEY = 'PRIVACY_MODE' -LED_MODE_KEY = 'LED' - -ATTR_MODE = 'mode' -ATTR_VALUE = 'value' -ATTR_DURATION = 'duration' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): - cv.time_period, -}) - -LOGI_CIRCLE_SERVICE_SET_CONFIG = CAMERA_SERVICE_SCHEMA.extend({ - vol.Required(ATTR_MODE): vol.In([BATTERY_SAVING_MODE_KEY, LED_MODE_KEY, - PRIVACY_MODE_KEY]), - vol.Required(ATTR_VALUE): cv.boolean -}) - -LOGI_CIRCLE_SERVICE_SNAPSHOT = CAMERA_SERVICE_SCHEMA.extend({ - vol.Required(ATTR_FILENAME): cv.template -}) - -LOGI_CIRCLE_SERVICE_RECORD = CAMERA_SERVICE_SCHEMA.extend({ - vol.Required(ATTR_FILENAME): cv.template, - vol.Required(ATTR_DURATION): cv.positive_int -}) - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up a Logi Circle Camera.""" - devices = hass.data[LOGI_CIRCLE_DOMAIN] + """Set up a Logi Circle Camera. Obsolete.""" + _LOGGER.warning( + "Logi Circle no longer works with camera platform configuration") - cameras = [] - for device in devices: - cameras.append(LogiCam(device, config)) - async_add_entities(cameras, True) +async def async_setup_entry(hass, entry, async_add_entities): + """Set up a Logi Circle Camera based on a config entry.""" + devices = await hass.data[LOGI_CIRCLE_DOMAIN].cameras + ffmpeg = hass.data[DATA_FFMPEG] - async def service_handler(service): - """Dispatch service calls to target entities.""" - params = {key: value for key, value in service.data.items() - if key != ATTR_ENTITY_ID} - entity_ids = service.data.get(ATTR_ENTITY_ID) - if entity_ids: - target_devices = [dev for dev in cameras - if dev.entity_id in entity_ids] - else: - target_devices = cameras - - for target_device in target_devices: - if service.service == SERVICE_SET_CONFIG: - await target_device.set_config(**params) - if service.service == SERVICE_LIVESTREAM_SNAPSHOT: - await target_device.livestream_snapshot(**params) - if service.service == SERVICE_LIVESTREAM_RECORD: - await target_device.download_livestream(**params) - - hass.services.async_register( - DOMAIN, SERVICE_SET_CONFIG, service_handler, - schema=LOGI_CIRCLE_SERVICE_SET_CONFIG) - - hass.services.async_register( - DOMAIN, SERVICE_LIVESTREAM_SNAPSHOT, service_handler, - schema=LOGI_CIRCLE_SERVICE_SNAPSHOT) - - hass.services.async_register( - DOMAIN, SERVICE_LIVESTREAM_RECORD, service_handler, - schema=LOGI_CIRCLE_SERVICE_RECORD) + cameras = [LogiCam(device, entry, ffmpeg) + for device in devices] + + async_add_entities(cameras, True) class LogiCam(Camera): """An implementation of a Logi Circle camera.""" - def __init__(self, camera, device_info): + def __init__(self, camera, device_info, ffmpeg): """Initialize Logi Circle camera.""" super().__init__() self._camera = camera self._name = self._camera.name self._id = self._camera.mac_address self._has_battery = self._camera.supports_feature('battery_level') + self._ffmpeg = ffmpeg + self._listeners = [] + + async def async_added_to_hass(self): + """Connect camera methods to signals.""" + def _dispatch_proxy(method): + """Expand parameters & filter entity IDs.""" + async def _call(params): + entity_ids = params.get(ATTR_ENTITY_ID) + filtered_params = {k: v for k, + v in params.items() if k != ATTR_ENTITY_ID} + if entity_ids is None or self.entity_id in entity_ids: + await method(**filtered_params) + return _call + + self._listeners.extend([ + async_dispatcher_connect( + self.hass, + SIGNAL_LOGI_CIRCLE_RECONFIGURE, + _dispatch_proxy(self.set_config)), + async_dispatcher_connect( + self.hass, + SIGNAL_LOGI_CIRCLE_SNAPSHOT, + _dispatch_proxy(self.livestream_snapshot)), + async_dispatcher_connect( + self.hass, + SIGNAL_LOGI_CIRCLE_RECORD, + _dispatch_proxy(self.download_livestream)), + ]) + + async def async_will_remove_from_hass(self): + """Disconnect dispatcher listeners when removed.""" + for detach in self._listeners: + detach() @property def unique_id(self): @@ -132,20 +107,19 @@ def device_state_attributes(self): ATTR_ATTRIBUTION: ATTRIBUTION, 'battery_saving_mode': ( STATE_ON if self._camera.battery_saving else STATE_OFF), - 'ip_address': self._camera.ip_address, 'microphone_gain': self._camera.microphone_gain } # Add battery attributes if camera is battery-powered if self._has_battery: - state[ATTR_BATTERY_CHARGING] = self._camera.is_charging + state[ATTR_BATTERY_CHARGING] = self._camera.charging state[ATTR_BATTERY_LEVEL] = self._camera.battery_level return state async def async_camera_image(self): """Return a still image from the camera.""" - return await self._camera.get_snapshot_image() + return await self._camera.live_stream.download_jpeg() async def async_turn_off(self): """Disable streaming mode for this camera.""" @@ -163,11 +137,9 @@ def should_poll(self): async def set_config(self, mode, value): """Set an configuration property for the target camera.""" if mode == LED_MODE_KEY: - await self._camera.set_led(value) - if mode == PRIVACY_MODE_KEY: - await self._camera.set_privacy_mode(value) - if mode == BATTERY_SAVING_MODE_KEY: - await self._camera.set_battery_saving_mode(value) + await self._camera.set_config('led', value) + if mode == RECORDING_MODE_KEY: + await self._camera.set_config('recording_disabled', not value) async def download_livestream(self, filename, duration): """Download a recording from the camera's livestream.""" @@ -182,8 +154,10 @@ async def download_livestream(self, filename, duration): "Can't write %s, no access to path!", stream_file) return - asyncio.shield(self._camera.record_livestream( - stream_file, timedelta(seconds=duration)), loop=self.hass.loop) + await self._camera.live_stream.download_rtsp( + filename=stream_file, + duration=timedelta(seconds=duration), + ffmpeg_bin=self._ffmpeg.binary) async def livestream_snapshot(self, filename): """Download a still frame from the camera's livestream.""" @@ -198,8 +172,9 @@ async def livestream_snapshot(self, filename): "Can't write %s, no access to path!", snapshot_file) return - asyncio.shield(self._camera.get_livestream_image( - snapshot_file), loop=self.hass.loop) + await self._camera.live_stream.download_jpeg( + filename=snapshot_file, + refresh=True) async def async_update(self): """Update camera entity and refresh attributes.""" diff --git a/homeassistant/components/logi_circle/config_flow.py b/homeassistant/components/logi_circle/config_flow.py new file mode 100644 index 00000000000000..ba772fb0fed23a --- /dev/null +++ b/homeassistant/components/logi_circle/config_flow.py @@ -0,0 +1,205 @@ +"""Config flow to configure Logi Circle component.""" +import asyncio +from collections import OrderedDict + +import async_timeout +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.components.http import HomeAssistantView +from homeassistant.const import CONF_SENSORS +from homeassistant.core import callback + +from .const import ( + CONF_API_KEY, CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_REDIRECT_URI, + DEFAULT_CACHEDB, DOMAIN) + +_TIMEOUT = 15 # seconds + +DATA_FLOW_IMPL = 'logi_circle_flow_implementation' +EXTERNAL_ERRORS = 'logi_errors' +AUTH_CALLBACK_PATH = '/api/logi_circle' +AUTH_CALLBACK_NAME = 'api:logi_circle' + + +@callback +def register_flow_implementation(hass, domain, client_id, client_secret, + api_key, redirect_uri, sensors): + """Register a flow implementation. + + domain: Domain of the component responsible for the implementation. + client_id: Client ID. + client_secret: Client secret. + api_key: API key issued by Logitech. + redirect_uri: Auth callback redirect URI. + sensors: Sensor config. + """ + if DATA_FLOW_IMPL not in hass.data: + hass.data[DATA_FLOW_IMPL] = OrderedDict() + + hass.data[DATA_FLOW_IMPL][domain] = { + CONF_CLIENT_ID: client_id, + CONF_CLIENT_SECRET: client_secret, + CONF_API_KEY: api_key, + CONF_REDIRECT_URI: redirect_uri, + CONF_SENSORS: sensors, + EXTERNAL_ERRORS: None + } + + +@config_entries.HANDLERS.register(DOMAIN) +class LogiCircleFlowHandler(config_entries.ConfigFlow): + """Config flow for Logi Circle component.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL + + def __init__(self): + """Initialize flow.""" + self.flow_impl = None + + async def async_step_import(self, user_input=None): + """Handle external yaml configuration.""" + if self.hass.config_entries.async_entries(DOMAIN): + return self.async_abort(reason='already_setup') + + self.flow_impl = DOMAIN + + return await self.async_step_auth() + + async def async_step_user(self, user_input=None): + """Handle a flow start.""" + flows = self.hass.data.get(DATA_FLOW_IMPL, {}) + + if self.hass.config_entries.async_entries(DOMAIN): + return self.async_abort(reason='already_setup') + + if not flows: + return self.async_abort(reason='no_flows') + + if len(flows) == 1: + self.flow_impl = list(flows)[0] + return await self.async_step_auth() + + if user_input is not None: + self.flow_impl = user_input['flow_impl'] + return await self.async_step_auth() + + return self.async_show_form( + step_id='user', + data_schema=vol.Schema({ + vol.Required('flow_impl'): + vol.In(list(flows)) + })) + + async def async_step_auth(self, user_input=None): + """Create an entry for auth.""" + if self.hass.config_entries.async_entries(DOMAIN): + return self.async_abort(reason='external_setup') + + external_error = (self.hass.data[DATA_FLOW_IMPL][DOMAIN] + [EXTERNAL_ERRORS]) + errors = {} + if external_error: + # Handle error from another flow + errors['base'] = external_error + self.hass.data[DATA_FLOW_IMPL][DOMAIN][EXTERNAL_ERRORS] = None + elif user_input is not None: + errors['base'] = 'follow_link' + + url = self._get_authorization_url() + + return self.async_show_form( + step_id='auth', + description_placeholders={'authorization_url': url}, + errors=errors) + + def _get_authorization_url(self): + """Create temporary Circle session and generate authorization url.""" + from logi_circle import LogiCircle + flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl] + client_id = flow[CONF_CLIENT_ID] + client_secret = flow[CONF_CLIENT_SECRET] + api_key = flow[CONF_API_KEY] + redirect_uri = flow[CONF_REDIRECT_URI] + + logi_session = LogiCircle( + client_id=client_id, + client_secret=client_secret, + api_key=api_key, + redirect_uri=redirect_uri) + + self.hass.http.register_view(LogiCircleAuthCallbackView()) + + return logi_session.authorize_url + + async def async_step_code(self, code=None): + """Received code for authentication.""" + if self.hass.config_entries.async_entries(DOMAIN): + return self.async_abort(reason='already_setup') + + return await self._async_create_session(code) + + async def _async_create_session(self, code): + """Create Logi Circle session and entries.""" + from logi_circle import LogiCircle + from logi_circle.exception import AuthorizationFailed + + flow = self.hass.data[DATA_FLOW_IMPL][DOMAIN] + client_id = flow[CONF_CLIENT_ID] + client_secret = flow[CONF_CLIENT_SECRET] + api_key = flow[CONF_API_KEY] + redirect_uri = flow[CONF_REDIRECT_URI] + sensors = flow[CONF_SENSORS] + + logi_session = LogiCircle( + client_id=client_id, + client_secret=client_secret, + api_key=api_key, + redirect_uri=redirect_uri, + cache_file=DEFAULT_CACHEDB) + + try: + with async_timeout.timeout(_TIMEOUT, loop=self.hass.loop): + await logi_session.authorize(code) + except AuthorizationFailed: + (self.hass.data[DATA_FLOW_IMPL][DOMAIN] + [EXTERNAL_ERRORS]) = 'auth_error' + return self.async_abort(reason='external_error') + except asyncio.TimeoutError: + (self.hass.data[DATA_FLOW_IMPL][DOMAIN] + [EXTERNAL_ERRORS]) = 'auth_timeout' + return self.async_abort(reason='external_error') + + account_id = (await logi_session.account)['accountId'] + await logi_session.close() + return self.async_create_entry( + title='Logi Circle ({})'.format(account_id), + data={ + CONF_CLIENT_ID: client_id, + CONF_CLIENT_SECRET: client_secret, + CONF_API_KEY: api_key, + CONF_REDIRECT_URI: redirect_uri, + CONF_SENSORS: sensors}) + + +class LogiCircleAuthCallbackView(HomeAssistantView): + """Logi Circle Authorization Callback View.""" + + requires_auth = False + url = AUTH_CALLBACK_PATH + name = AUTH_CALLBACK_NAME + + async def get(self, request): + """Receive authorization code.""" + hass = request.app['hass'] + if 'code' in request.query: + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={'source': 'code'}, + data=request.query['code'], + )) + return self.json_message("Authorisation code saved") + return self.json_message("Authorisation code missing " + "from query string", status_code=400) diff --git a/homeassistant/components/logi_circle/const.py b/homeassistant/components/logi_circle/const.py new file mode 100644 index 00000000000000..3b32e8139e0a4f --- /dev/null +++ b/homeassistant/components/logi_circle/const.py @@ -0,0 +1,43 @@ +"""Constants in Logi Circle component.""" + +CONF_CLIENT_ID = 'client_id' +CONF_CLIENT_SECRET = 'client_secret' +CONF_API_KEY = 'api_key' +CONF_REDIRECT_URI = 'redirect_uri' + +DEFAULT_CACHEDB = '.logi_cache.pickle' + +DOMAIN = 'logi_circle' +DATA_LOGI = DOMAIN + +LED_MODE_KEY = 'LED' +RECORDING_MODE_KEY = 'RECORDING_MODE' + +# Sensor types: Name, unit of measure, icon per sensor key. +LOGI_SENSORS = { + 'battery_level': [ + 'Battery', '%', 'battery-50'], + + 'last_activity_time': [ + "Last Activity", None, 'history'], + + 'recording': [ + 'Recording Mode', None, 'eye'], + + 'signal_strength_category': [ + "WiFi Signal Category", None, 'wifi'], + + 'signal_strength_percentage': [ + "WiFi Signal Strength", '%', 'wifi'], + + 'streaming': [ + 'Streaming Mode', None, 'camera'], +} + +SIGNAL_LOGI_CIRCLE_RECONFIGURE = 'logi_circle_reconfigure' +SIGNAL_LOGI_CIRCLE_SNAPSHOT = 'logi_circle_snapshot' +SIGNAL_LOGI_CIRCLE_RECORD = 'logi_circle_record' + +# Attribution +ATTRIBUTION = "Data provided by circle.logi.com" +DEVICE_BRAND = 'Logitech' diff --git a/homeassistant/components/logi_circle/manifest.json b/homeassistant/components/logi_circle/manifest.json index 3e8281d5a9cace..8cf6a157a01a57 100644 --- a/homeassistant/components/logi_circle/manifest.json +++ b/homeassistant/components/logi_circle/manifest.json @@ -1,10 +1,8 @@ { "domain": "logi_circle", - "name": "Logi circle", + "name": "Logi Circle", "documentation": "https://www.home-assistant.io/components/logi_circle", - "requirements": [ - "logi_circle==0.1.7" - ], - "dependencies": [], - "codeowners": [] + "requirements": ["logi_circle==0.2.2"], + "dependencies": ["ffmpeg"], + "codeowners": ["@evanjd"] } diff --git a/homeassistant/components/logi_circle/sensor.py b/homeassistant/components/logi_circle/sensor.py index 06d1701a9ebd12..01d5492eea7ce7 100644 --- a/homeassistant/components/logi_circle/sensor.py +++ b/homeassistant/components/logi_circle/sensor.py @@ -1,51 +1,36 @@ """Support for Logi Circle sensors.""" import logging -import voluptuous as vol - -from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, CONF_ENTITY_NAMESPACE, - CONF_MONITORED_CONDITIONS, STATE_OFF, STATE_ON) -import homeassistant.helpers.config_validation as cv + ATTR_ATTRIBUTION, ATTR_BATTERY_CHARGING, CONF_MONITORED_CONDITIONS, + CONF_SENSORS, STATE_OFF, STATE_ON) from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.util.dt import as_local -from . import ( - ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DOMAIN as LOGI_CIRCLE_DOMAIN) +from .const import ( + ATTRIBUTION, DOMAIN as LOGI_CIRCLE_DOMAIN, LOGI_SENSORS as SENSOR_TYPES) DEPENDENCIES = ['logi_circle'] _LOGGER = logging.getLogger(__name__) -# Sensor types: Name, unit of measure, icon per sensor key. -SENSOR_TYPES = { - 'battery_level': ['Battery', '%', 'battery-50'], - 'last_activity_time': ['Last Activity', None, 'history'], - 'privacy_mode': ['Privacy Mode', None, 'eye'], - 'signal_strength_category': ['WiFi Signal Category', None, 'wifi'], - 'signal_strength_percentage': ['WiFi Signal Strength', '%', 'wifi'], - 'speaker_volume': ['Volume', '%', 'volume-high'], - 'streaming_mode': ['Streaming Mode', None, 'camera'], -} - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE): - cv.string, - vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): - vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), -}) - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up a sensor for a Logi Circle device.""" - devices = hass.data[LOGI_CIRCLE_DOMAIN] + """Set up a sensor for a Logi Circle device. Obsolete.""" + _LOGGER.warning( + 'Logi Circle no longer works with sensor platform configuration') + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up a Logi Circle sensor based on a config entry.""" + devices = await hass.data[LOGI_CIRCLE_DOMAIN].cameras time_zone = str(hass.config.time_zone) sensors = [] - for sensor_type in config.get(CONF_MONITORED_CONDITIONS): + for sensor_type in (entry.data.get(CONF_SENSORS) + .get(CONF_MONITORED_CONDITIONS)): for device in devices: if device.supports_feature(sensor_type): sensors.append(LogiSensor(device, time_zone, sensor_type)) @@ -64,6 +49,7 @@ def __init__(self, camera, time_zone, sensor_type): self._icon = 'mdi:{}'.format(SENSOR_TYPES.get(self._sensor_type)[2]) self._name = "{0} {1}".format( self._camera.name, SENSOR_TYPES.get(self._sensor_type)[0]) + self._activity = {} self._state = None self._tz = time_zone @@ -89,12 +75,11 @@ def device_state_attributes(self): ATTR_ATTRIBUTION: ATTRIBUTION, 'battery_saving_mode': ( STATE_ON if self._camera.battery_saving else STATE_OFF), - 'ip_address': self._camera.ip_address, 'microphone_gain': self._camera.microphone_gain } if self._sensor_type == 'battery_level': - state[ATTR_BATTERY_CHARGING] = self._camera.is_charging + state[ATTR_BATTERY_CHARGING] = self._camera.charging return state @@ -105,9 +90,9 @@ def icon(self): self._state is not None): return icon_for_battery_level(battery_level=int(self._state), charging=False) - if (self._sensor_type == 'privacy_mode' and + if (self._sensor_type == 'recording_mode' and self._state is not None): - return 'mdi:eye-off' if self._state == STATE_ON else 'mdi:eye' + return 'mdi:eye' if self._state == STATE_ON else 'mdi:eye-off' if (self._sensor_type == 'streaming_mode' and self._state is not None): return ( @@ -125,7 +110,8 @@ async def async_update(self): await self._camera.update() if self._sensor_type == 'last_activity_time': - last_activity = await self._camera.last_activity + last_activity = (await self._camera. + get_last_activity(force_refresh=True)) if last_activity is not None: last_activity_time = as_local(last_activity.end_time_utc) self._state = '{0:0>2}:{1:0>2}'.format( @@ -136,3 +122,4 @@ async def async_update(self): self._state = STATE_ON if state is True else STATE_OFF else: self._state = state + self._state = state diff --git a/homeassistant/components/logi_circle/services.yaml b/homeassistant/components/logi_circle/services.yaml new file mode 100644 index 00000000000000..8d1c7ca1485f9f --- /dev/null +++ b/homeassistant/components/logi_circle/services.yaml @@ -0,0 +1,37 @@ +# Describes the format for available Logi Circle services + +set_config: + description: Set a configuration property. + fields: + entity_id: + description: Name(s) of entities to apply the operation mode to. + example: "camera.living_room_camera" + mode: + description: "Operation mode. Allowed values: LED, RECORDING_MODE." + example: "RECORDING_MODE" + value: + description: "Operation value. Allowed values: true, false" + example: true + +livestream_snapshot: + description: Take a snapshot from the camera's livestream. Will wake the camera from sleep if required. + fields: + entity_id: + description: Name(s) of entities to create snapshots from. + example: "camera.living_room_camera" + filename: + description: Template of a Filename. Variable is entity_id. + example: "/tmp/snapshot_{{ entity_id }}.jpg" + +livestream_record: + description: Take a video recording from the camera's livestream. + fields: + entity_id: + description: Name(s) of entities to create recordings from. + example: "camera.living_room_camera" + filename: + description: Template of a Filename. Variable is entity_id. + example: "/tmp/snapshot_{{ entity_id }}.mp4" + duration: + description: Recording duration in seconds. + example: 60 diff --git a/homeassistant/components/logi_circle/strings.json b/homeassistant/components/logi_circle/strings.json new file mode 100644 index 00000000000000..57dd0b709b7102 --- /dev/null +++ b/homeassistant/components/logi_circle/strings.json @@ -0,0 +1,32 @@ +{ + "config": { + "title": "Logi Circle", + "step": { + "user": { + "title": "Authentication Provider", + "description": "Pick via which authentication provider you want to authenticate with Logi Circle.", + "data": { + "flow_impl": "Provider" + } + }, + "auth": { + "title": "Authenticate with Logi Circle", + "description": "Please follow the link below and Accept access to your Logi Circle account, then come back and press Submit below.\n\n[Link]({authorization_url})" + } + }, + "create_entry": { + "default": "Successfully authenticated with Logi Circle." + }, + "error": { + "auth_error": "API authorization failed.", + "auth_timeout": "Authorization timed out when requesting access token.", + "follow_link": "Please follow the link and authenticate before pressing Submit." + }, + "abort": { + "already_setup": "You can only configure a single Logi Circle account.", + "external_error": "Exception occurred from another flow.", + "external_setup": "Logi Circle successfully configured from another flow.", + "no_flows": "You need to configure Logi Circle before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/logi_circle/)." + } + } +} diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index d0d48c0f764076..571cacbaf8fef2 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -161,6 +161,7 @@ async def async_step_discovery(info): 'ipma', 'lifx', 'locative', + 'logi_circle', 'luftdaten', 'mailgun', 'mobile_app', diff --git a/requirements_all.txt b/requirements_all.txt index 4d2cfcf8034c09..8c92d73445f2b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -653,7 +653,7 @@ lmnotify==0.0.4 locationsharinglib==3.0.11 # homeassistant.components.logi_circle -logi_circle==0.1.7 +logi_circle==0.2.2 # homeassistant.components.london_underground london-tube-status==0.2 diff --git a/tests/components/logi_circle/__init__.py b/tests/components/logi_circle/__init__.py new file mode 100644 index 00000000000000..d2e2fbb8fdb2a7 --- /dev/null +++ b/tests/components/logi_circle/__init__.py @@ -0,0 +1 @@ +"""Tests for the Logi Circle component.""" diff --git a/tests/components/logi_circle/test_config_flow.py b/tests/components/logi_circle/test_config_flow.py new file mode 100644 index 00000000000000..048072ec4e0711 --- /dev/null +++ b/tests/components/logi_circle/test_config_flow.py @@ -0,0 +1,201 @@ +"""Tests for Logi Circle config flow.""" +import asyncio +from unittest.mock import Mock, patch + +import pytest + +from homeassistant import data_entry_flow +from homeassistant.components.logi_circle import config_flow +from homeassistant.components.logi_circle.config_flow import ( + DOMAIN, LogiCircleAuthCallbackView) +from homeassistant.setup import async_setup_component + +from tests.common import MockDependency, mock_coro + + +class AuthorizationFailed(Exception): + """Dummy Exception.""" + + +class MockRequest(): + """Mock request passed to HomeAssistantView.""" + + def __init__(self, hass, query): + """Init request object.""" + self.app = {"hass": hass} + self.query = query + + +def init_config_flow(hass): + """Init a configuration flow.""" + config_flow.register_flow_implementation(hass, + DOMAIN, + client_id='id', + client_secret='secret', + api_key='123', + redirect_uri='http://example.com', + sensors=None) + flow = config_flow.LogiCircleFlowHandler() + flow._get_authorization_url = Mock( # pylint: disable=W0212 + return_value='http://example.com') + flow.hass = hass + return flow + + +@pytest.fixture +def mock_logi_circle(): + """Mock logi_circle.""" + with MockDependency('logi_circle', 'exception') as mock_logi_circle_: + mock_logi_circle_.exception.AuthorizationFailed = AuthorizationFailed + mock_logi_circle_.LogiCircle().authorize = Mock( + return_value=mock_coro(return_value=True)) + mock_logi_circle_.LogiCircle().close = Mock( + return_value=mock_coro(return_value=True)) + mock_logi_circle_.LogiCircle().account = mock_coro( + return_value={'accountId': 'testId'}) + mock_logi_circle_.LogiCircle().authorize_url = 'http://authorize.url' + yield mock_logi_circle_ + + +async def test_step_import(hass, mock_logi_circle): # pylint: disable=W0621 + """Test that we trigger import when configuring with client.""" + flow = init_config_flow(hass) + + result = await flow.async_step_import() + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'auth' + + +async def test_full_flow_implementation(hass, mock_logi_circle): # noqa pylint: disable=W0621 + """Test registering an implementation and finishing flow works.""" + config_flow.register_flow_implementation( + hass, + 'test-other', + client_id=None, + client_secret=None, + api_key=None, + redirect_uri=None, + sensors=None) + flow = init_config_flow(hass) + + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'user' + + result = await flow.async_step_user({'flow_impl': 'test-other'}) + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'auth' + assert result['description_placeholders'] == { + 'authorization_url': 'http://example.com', + } + + result = await flow.async_step_code('123ABC') + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'Logi Circle ({})'.format('testId') + + +async def test_we_reprompt_user_to_follow_link(hass): + """Test we prompt user to follow link if previously prompted.""" + flow = init_config_flow(hass) + + result = await flow.async_step_auth('dummy') + assert result['errors']['base'] == 'follow_link' + + +async def test_abort_if_no_implementation_registered(hass): + """Test we abort if no implementation is registered.""" + flow = config_flow.LogiCircleFlowHandler() + flow.hass = hass + + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'no_flows' + + +async def test_abort_if_already_setup(hass): + """Test we abort if Logi Circle is already setup.""" + flow = init_config_flow(hass) + + with patch.object(hass.config_entries, 'async_entries', return_value=[{}]): + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'already_setup' + + with patch.object(hass.config_entries, 'async_entries', return_value=[{}]): + result = await flow.async_step_import() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'already_setup' + + with patch.object(hass.config_entries, 'async_entries', return_value=[{}]): + result = await flow.async_step_code() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'already_setup' + + with patch.object(hass.config_entries, 'async_entries', return_value=[{}]): + result = await flow.async_step_auth() + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'external_setup' + + +@pytest.mark.parametrize('side_effect,error', + [(asyncio.TimeoutError, 'auth_timeout'), + (AuthorizationFailed, 'auth_error')]) +async def test_abort_if_authorize_fails(hass, mock_logi_circle, side_effect, error): # noqa pylint: disable=W0621 + """Test we abort if authorizing fails.""" + flow = init_config_flow(hass) + mock_logi_circle.LogiCircle().authorize.side_effect = side_effect + + result = await flow.async_step_code('123ABC') + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'external_error' + + result = await flow.async_step_auth() + assert result['errors']['base'] == error + + +async def test_not_pick_implementation_if_only_one(hass): + """Test we bypass picking implementation if we have one flow_imp.""" + flow = init_config_flow(hass) + + result = await flow.async_step_user() + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'auth' + + +async def test_gen_auth_url(hass, mock_logi_circle): # pylint: disable=W0621 + """Test generating authorize URL from Logi Circle API.""" + config_flow.register_flow_implementation(hass, + 'test-auth-url', + client_id='id', + client_secret='secret', + api_key='123', + redirect_uri='http://example.com', + sensors=None) + flow = config_flow.LogiCircleFlowHandler() + flow.hass = hass + flow.flow_impl = 'test-auth-url' + await async_setup_component(hass, 'http') + + result = flow._get_authorization_url() # pylint: disable=W0212 + assert result == 'http://authorize.url' + + +async def test_callback_view_rejects_missing_code(hass): + """Test the auth callback view rejects requests with no code.""" + view = LogiCircleAuthCallbackView() + resp = await view.get(MockRequest(hass, {})) + + assert resp.status == 400 + + +async def test_callback_view_accepts_code(hass, mock_logi_circle): # noqa pylint: disable=W0621 + """Test the auth callback view handles requests with auth code.""" + init_config_flow(hass) + view = LogiCircleAuthCallbackView() + + resp = await view.get(MockRequest(hass, {"code": "456"})) + assert resp.status == 200 + + await hass.async_block_till_done() + mock_logi_circle.LogiCircle.return_value.authorize.assert_called_with( + '456') From 34bb31f4ecc44a25e0d13f6cfc87bb85fddd147c Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Tue, 9 Apr 2019 08:21:47 -0500 Subject: [PATCH 528/605] Add amcrest binary_sensors (#22703) * Add amcrest binary_sensors Add binary_sensors with option motion_detected. Deprecate motion_detector sensor. * Update per review * Update per review Add custom validators to make sure camera names are unique, and to issue warning if deprecated sensors option motion_detector is used. async_setup_platform should not return a value. * Another review update Since there is only one type of binary_sensor, remove type test in update method. --- homeassistant/components/amcrest/__init__.py | 119 +++++++++++------- .../components/amcrest/binary_sensor.py | 71 +++++++++++ homeassistant/components/amcrest/camera.py | 2 - homeassistant/components/amcrest/sensor.py | 1 - 4 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 homeassistant/components/amcrest/binary_sensor.py diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index ff34ca6f5c7553..a4c020efcdfa13 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -7,11 +7,11 @@ from homeassistant.const import ( CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD, - CONF_SENSORS, CONF_SWITCHES, CONF_SCAN_INTERVAL, HTTP_BASIC_AUTHENTICATION) + CONF_BINARY_SENSORS, CONF_SENSORS, CONF_SWITCHES, CONF_SCAN_INTERVAL, + HTTP_BASIC_AUTHENTICATION) from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv - REQUIREMENTS = ['amcrest==1.3.0'] DEPENDENCIES = ['ffmpeg'] @@ -52,9 +52,14 @@ 'rtsp': 2, } +BINARY_SENSORS = { + 'motion_detected': 'Motion Detected' +} + # Sensor types are defined like: Name, units, icon +SENSOR_MOTION_DETECTOR = 'motion_detector' SENSORS = { - 'motion_detector': ['Motion Detected', None, 'mdi:run'], + SENSOR_MOTION_DETECTOR: ['Motion Detected', None, 'mdi:run'], 'sdcard': ['SD Used', '%', 'mdi:sd'], 'ptz_preset': ['PTZ Preset', None, 'mdi:camera-iris'], } @@ -65,28 +70,49 @@ 'motion_recording': ['Motion Recording', 'mdi:record-rec'] } + +def _deprecated_sensors(value): + if SENSOR_MOTION_DETECTOR in value: + _LOGGER.warning( + 'sensors option %s is deprecated. ' + 'Please remove from your configuration and ' + 'use binary_sensors option motion_detected instead.', + SENSOR_MOTION_DETECTOR) + return value + + +def _has_unique_names(value): + names = [camera[CONF_NAME] for camera in value] + vol.Schema(vol.Unique())(names) + return value + + +AMCREST_SCHEMA = vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): + vol.All(vol.In(AUTHENTICATION_LIST)), + vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION): + vol.All(vol.In(RESOLUTION_LIST)), + vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE): + vol.All(vol.In(STREAM_SOURCE_LIST)), + vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): + cv.string, + vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): + cv.time_period, + vol.Optional(CONF_BINARY_SENSORS): + vol.All(cv.ensure_list, [vol.In(BINARY_SENSORS)]), + vol.Optional(CONF_SENSORS): + vol.All(cv.ensure_list, [vol.In(SENSORS)], _deprecated_sensors), + vol.Optional(CONF_SWITCHES): + vol.All(cv.ensure_list, [vol.In(SWITCHES)]), +}) + CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.All(cv.ensure_list, [vol.Schema({ - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION): - vol.All(vol.In(AUTHENTICATION_LIST)), - vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION): - vol.All(vol.In(RESOLUTION_LIST)), - vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE): - vol.All(vol.In(STREAM_SOURCE_LIST)), - vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): - cv.string, - vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): - cv.time_period, - vol.Optional(CONF_SENSORS): - vol.All(cv.ensure_list, [vol.In(SENSORS)]), - vol.Optional(CONF_SWITCHES): - vol.All(cv.ensure_list, [vol.In(SWITCHES)]), - })]) + DOMAIN: vol.All(cv.ensure_list, [AMCREST_SCHEMA], _has_unique_names) }, extra=vol.ALLOW_EXTRA) @@ -94,20 +120,24 @@ def setup(hass, config): """Set up the Amcrest IP Camera component.""" from amcrest import AmcrestCamera, AmcrestError - hass.data[DATA_AMCREST] = {} + hass.data.setdefault(DATA_AMCREST, {}) amcrest_cams = config[DOMAIN] for device in amcrest_cams: + name = device[CONF_NAME] + username = device[CONF_USERNAME] + password = device[CONF_PASSWORD] + try: - camera = AmcrestCamera(device.get(CONF_HOST), - device.get(CONF_PORT), - device.get(CONF_USERNAME), - device.get(CONF_PASSWORD)).camera + camera = AmcrestCamera(device[CONF_HOST], + device[CONF_PORT], + username, + password).camera # pylint: disable=pointless-statement camera.current_time except AmcrestError as ex: - _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex)) + _LOGGER.error("Unable to connect to %s camera: %s", name, str(ex)) hass.components.persistent_notification.create( 'Error: {}
' 'You will need to restart hass after fixing.' @@ -116,23 +146,19 @@ def setup(hass, config): notification_id=NOTIFICATION_ID) continue - ffmpeg_arguments = device.get(CONF_FFMPEG_ARGUMENTS) - name = device.get(CONF_NAME) - resolution = RESOLUTION_LIST[device.get(CONF_RESOLUTION)] + ffmpeg_arguments = device[CONF_FFMPEG_ARGUMENTS] + resolution = RESOLUTION_LIST[device[CONF_RESOLUTION]] + binary_sensors = device.get(CONF_BINARY_SENSORS) sensors = device.get(CONF_SENSORS) switches = device.get(CONF_SWITCHES) - stream_source = STREAM_SOURCE_LIST[device.get(CONF_STREAM_SOURCE)] - - username = device.get(CONF_USERNAME) - password = device.get(CONF_PASSWORD) + stream_source = STREAM_SOURCE_LIST[device[CONF_STREAM_SOURCE]] # currently aiohttp only works with basic authentication # only valid for mjpeg streaming - if username is not None and password is not None: - if device.get(CONF_AUTHENTICATION) == HTTP_BASIC_AUTHENTICATION: - authentication = aiohttp.BasicAuth(username, password) - else: - authentication = None + if device[CONF_AUTHENTICATION] == HTTP_BASIC_AUTHENTICATION: + authentication = aiohttp.BasicAuth(username, password) + else: + authentication = None hass.data[DATA_AMCREST][name] = AmcrestDevice( camera, name, authentication, ffmpeg_arguments, stream_source, @@ -143,6 +169,13 @@ def setup(hass, config): CONF_NAME: name, }, config) + if binary_sensors: + discovery.load_platform( + hass, 'binary_sensor', DOMAIN, { + CONF_NAME: name, + CONF_BINARY_SENSORS: binary_sensors + }, config) + if sensors: discovery.load_platform( hass, 'sensor', DOMAIN, { @@ -157,7 +190,7 @@ def setup(hass, config): CONF_SWITCHES: switches }, config) - return True + return len(hass.data[DATA_AMCREST]) >= 1 class AmcrestDevice: diff --git a/homeassistant/components/amcrest/binary_sensor.py b/homeassistant/components/amcrest/binary_sensor.py new file mode 100644 index 00000000000000..113918ed041c3e --- /dev/null +++ b/homeassistant/components/amcrest/binary_sensor.py @@ -0,0 +1,71 @@ +"""Suppoort for Amcrest IP camera binary sensors.""" +from datetime import timedelta +import logging + +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, DEVICE_CLASS_MOTION) +from homeassistant.const import CONF_NAME, CONF_BINARY_SENSORS +from . import DATA_AMCREST, BINARY_SENSORS + +DEPENDENCIES = ['amcrest'] + +_LOGGER = logging.getLogger(__name__) + +SCAN_INTERVAL = timedelta(seconds=5) + + +async def async_setup_platform(hass, config, async_add_devices, + discovery_info=None): + """Set up a binary sensor for an Amcrest IP Camera.""" + if discovery_info is None: + return + + device_name = discovery_info[CONF_NAME] + binary_sensors = discovery_info[CONF_BINARY_SENSORS] + amcrest = hass.data[DATA_AMCREST][device_name] + + amcrest_binary_sensors = [] + for sensor_type in binary_sensors: + amcrest_binary_sensors.append( + AmcrestBinarySensor(amcrest.name, amcrest.device, sensor_type)) + + async_add_devices(amcrest_binary_sensors, True) + + +class AmcrestBinarySensor(BinarySensorDevice): + """Binary sensor for Amcrest camera.""" + + def __init__(self, name, camera, sensor_type): + """Initialize entity.""" + self._name = '{} {}'.format(name, BINARY_SENSORS[sensor_type]) + self._camera = camera + self._sensor_type = sensor_type + self._state = None + + @property + def name(self): + """Return entity name.""" + return self._name + + @property + def is_on(self): + """Return if entity is on.""" + return self._state + + @property + def device_class(self): + """Return device class.""" + return DEVICE_CLASS_MOTION + + def update(self): + """Update entity.""" + from amcrest import AmcrestError + + _LOGGER.debug('Pulling data from %s binary sensor', self._name) + + try: + self._state = self._camera.is_motion_detected + except AmcrestError as error: + _LOGGER.error( + 'Could not update %s binary sensor due to error: %s', + self.name, error) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 853d5404dab326..f361c4e0183e6b 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -28,8 +28,6 @@ async def async_setup_platform(hass, config, async_add_entities, async_add_entities([AmcrestCam(hass, amcrest)], True) - return True - class AmcrestCam(Camera): """An implementation of an Amcrest IP camera.""" diff --git a/homeassistant/components/amcrest/sensor.py b/homeassistant/components/amcrest/sensor.py index 68bc86da94c444..119520e6a03b5e 100644 --- a/homeassistant/components/amcrest/sensor.py +++ b/homeassistant/components/amcrest/sensor.py @@ -30,7 +30,6 @@ async def async_setup_platform( AmcrestSensor(amcrest.name, amcrest.device, sensor_type)) async_add_entities(amcrest_sensors, True) - return True class AmcrestSensor(Entity): From 0d2646ba25107ede35361293fb42a10eb6a108c9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 9 Apr 2019 08:34:20 -0700 Subject: [PATCH 529/605] Update translations --- .../ambient_station/.translations/es.json | 9 +++ .../ambient_station/.translations/fr.json | 3 +- .../ambient_station/.translations/ko.json | 2 +- .../ambient_station/.translations/th.json | 3 + .../components/auth/.translations/ko.json | 2 +- .../components/axis/.translations/es.json | 26 ++++++++ .../components/axis/.translations/fr.json | 26 ++++++++ .../components/axis/.translations/it.json | 25 ++++++++ .../components/axis/.translations/ko.json | 10 ++-- .../components/axis/.translations/pt.json | 4 +- .../components/axis/.translations/sv.json | 25 ++++++++ .../components/axis/.translations/th.json | 13 ++++ .../components/cast/.translations/ko.json | 2 +- .../components/cast/.translations/th.json | 9 ++- .../components/daikin/.translations/es.json | 19 ++++++ .../components/daikin/.translations/ko.json | 6 +- .../components/deconz/.translations/ca.json | 8 +++ .../components/deconz/.translations/de.json | 7 +++ .../components/deconz/.translations/en.json | 8 +++ .../components/deconz/.translations/es.json | 8 +++ .../components/deconz/.translations/it.json | 8 +++ .../components/deconz/.translations/ko.json | 10 +++- .../components/deconz/.translations/pl.json | 8 +++ .../components/deconz/.translations/ru.json | 8 +++ .../components/deconz/.translations/sl.json | 8 +++ .../deconz/.translations/zh-Hant.json | 8 +++ .../dialogflow/.translations/fr.json | 18 ++++++ .../dialogflow/.translations/ru.json | 2 +- .../components/ebusd/.translations/th.json | 1 + .../components/esphome/.translations/es.json | 4 ++ .../components/hangouts/.translations/ca.json | 1 + .../components/hangouts/.translations/de.json | 1 + .../components/hangouts/.translations/es.json | 1 + .../components/hangouts/.translations/ko.json | 1 + .../components/hangouts/.translations/pl.json | 1 + .../components/hangouts/.translations/ru.json | 3 +- .../components/hangouts/.translations/sl.json | 1 + .../components/hangouts/.translations/th.json | 1 + .../hangouts/.translations/zh-Hant.json | 1 + .../components/heos/.translations/de.json | 20 +++++++ .../components/heos/.translations/es.json | 20 +++++++ .../components/heos/.translations/fr.json | 20 +++++++ .../components/heos/.translations/it.json | 17 ++++++ .../components/heos/.translations/ko.json | 20 +++++++ .../components/heos/.translations/no.json | 15 +++++ .../components/heos/.translations/pl.json | 20 +++++++ .../components/heos/.translations/pt.json | 12 ++++ .../components/heos/.translations/sl.json | 20 +++++++ .../components/heos/.translations/sv.json | 17 ++++++ .../heos/.translations/zh-Hant.json | 20 +++++++ .../homekit_controller/.translations/it.json | 29 +++++++++ .../homekit_controller/.translations/ko.json | 8 +-- .../homekit_controller/.translations/pt.json | 16 +++++ .../homekit_controller/.translations/sv.json | 20 +++++++ .../homematicip_cloud/.translations/ko.json | 4 +- .../components/hue/.translations/th.json | 6 ++ .../components/ifttt/.translations/ru.json | 2 +- .../components/ipma/.translations/fr.json | 5 ++ .../components/lifx/.translations/ko.json | 2 +- .../components/locative/.translations/es.json | 14 +++++ .../logi_circle/.translations/ca.json | 32 ++++++++++ .../logi_circle/.translations/en.json | 60 +++++++++---------- .../logi_circle/.translations/ru.json | 32 ++++++++++ .../luftdaten/.translations/fr.json | 3 +- .../components/mailgun/.translations/fr.json | 6 +- .../mobile_app/.translations/es.json | 14 +++++ .../mobile_app/.translations/fr.json | 14 +++++ .../mobile_app/.translations/it.json | 10 ++++ .../mobile_app/.translations/pt.json | 10 ++++ .../mobile_app/.translations/sv.json | 9 +++ .../mobile_app/.translations/zh-Hans.json | 12 ++++ .../moon/.translations/sensor.es.json | 10 ++-- .../moon/.translations/sensor.pt.json | 7 +-- .../moon/.translations/sensor.th.json | 5 ++ .../components/mqtt/.translations/ko.json | 2 +- .../components/mqtt/.translations/ru.json | 4 +- .../components/mqtt/.translations/th.json | 4 +- .../components/nest/.translations/ca.json | 2 +- .../components/nest/.translations/ru.json | 4 +- .../components/nest/.translations/th.json | 15 +++++ .../components/openuv/.translations/ko.json | 2 +- .../components/openuv/.translations/ru.json | 2 +- .../owntracks/.translations/fr.json | 3 + .../components/point/.translations/ca.json | 6 +- .../components/point/.translations/fr.json | 15 ++++- .../components/point/.translations/ko.json | 2 +- .../components/point/.translations/ru.json | 12 ++-- .../components/ps4/.translations/es.json | 11 ++++ .../components/ps4/.translations/fr.json | 9 +++ .../components/ps4/.translations/it.json | 7 +++ .../components/ps4/.translations/ko.json | 2 +- .../components/ps4/.translations/pt.json | 4 ++ .../components/ps4/.translations/ru.json | 2 +- .../components/ps4/.translations/sv.json | 6 ++ .../components/ps4/.translations/th.json | 3 + .../rainmachine/.translations/ko.json | 2 +- .../season/.translations/sensor.zh-Hans.json | 8 +-- .../sensor/.translations/moon.th.json | 5 ++ .../simplisafe/.translations/ko.json | 2 +- .../smartthings/.translations/es.json | 9 ++- .../smartthings/.translations/ru.json | 2 +- .../components/sonos/.translations/ko.json | 2 +- .../tellduslive/.translations/ko.json | 4 +- .../components/toon/.translations/es.json | 34 +++++++++++ .../components/toon/.translations/pt.json | 12 ++++ .../components/toon/.translations/ru.json | 2 +- .../components/toon/.translations/th.json | 12 ++++ .../components/tplink/.translations/ko.json | 4 +- .../components/tplink/.translations/pt.json | 10 ++++ .../components/tplink/.translations/th.json | 5 ++ .../components/tradfri/.translations/ru.json | 2 +- .../components/unifi/.translations/fr.json | 1 + .../components/unifi/.translations/th.json | 3 +- .../components/upnp/.translations/fr.json | 1 + .../components/zha/.translations/fr.json | 1 + .../components/zha/.translations/ko.json | 2 +- .../components/zone/.translations/th.json | 17 ++++++ 117 files changed, 980 insertions(+), 109 deletions(-) create mode 100644 homeassistant/components/axis/.translations/es.json create mode 100644 homeassistant/components/axis/.translations/fr.json create mode 100644 homeassistant/components/axis/.translations/it.json create mode 100644 homeassistant/components/axis/.translations/sv.json create mode 100644 homeassistant/components/axis/.translations/th.json create mode 100644 homeassistant/components/daikin/.translations/es.json create mode 100644 homeassistant/components/dialogflow/.translations/fr.json create mode 100644 homeassistant/components/heos/.translations/de.json create mode 100644 homeassistant/components/heos/.translations/es.json create mode 100644 homeassistant/components/heos/.translations/fr.json create mode 100644 homeassistant/components/heos/.translations/it.json create mode 100644 homeassistant/components/heos/.translations/ko.json create mode 100644 homeassistant/components/heos/.translations/pl.json create mode 100644 homeassistant/components/heos/.translations/pt.json create mode 100644 homeassistant/components/heos/.translations/sl.json create mode 100644 homeassistant/components/heos/.translations/sv.json create mode 100644 homeassistant/components/heos/.translations/zh-Hant.json create mode 100644 homeassistant/components/homekit_controller/.translations/it.json create mode 100644 homeassistant/components/homekit_controller/.translations/pt.json create mode 100644 homeassistant/components/homekit_controller/.translations/sv.json create mode 100644 homeassistant/components/locative/.translations/es.json create mode 100644 homeassistant/components/logi_circle/.translations/ca.json create mode 100644 homeassistant/components/logi_circle/.translations/ru.json create mode 100644 homeassistant/components/mobile_app/.translations/es.json create mode 100644 homeassistant/components/mobile_app/.translations/fr.json create mode 100644 homeassistant/components/mobile_app/.translations/it.json create mode 100644 homeassistant/components/mobile_app/.translations/pt.json create mode 100644 homeassistant/components/mobile_app/.translations/sv.json create mode 100644 homeassistant/components/mobile_app/.translations/zh-Hans.json create mode 100644 homeassistant/components/moon/.translations/sensor.th.json create mode 100644 homeassistant/components/nest/.translations/th.json create mode 100644 homeassistant/components/sensor/.translations/moon.th.json create mode 100644 homeassistant/components/toon/.translations/es.json create mode 100644 homeassistant/components/toon/.translations/pt.json create mode 100644 homeassistant/components/toon/.translations/th.json create mode 100644 homeassistant/components/tplink/.translations/pt.json create mode 100644 homeassistant/components/tplink/.translations/th.json create mode 100644 homeassistant/components/zone/.translations/th.json diff --git a/homeassistant/components/ambient_station/.translations/es.json b/homeassistant/components/ambient_station/.translations/es.json index d6732423a7ec6b..91e203b6b2dba9 100644 --- a/homeassistant/components/ambient_station/.translations/es.json +++ b/homeassistant/components/ambient_station/.translations/es.json @@ -1,7 +1,16 @@ { "config": { + "error": { + "identifier_exists": "La clave API y/o la clave de aplicaci\u00f3n ya est\u00e1 registrada", + "invalid_key": "Clave API y/o clave de aplicaci\u00f3n no v\u00e1lida", + "no_devices": "No se han encontrado dispositivos en la cuenta" + }, "step": { "user": { + "data": { + "api_key": "Clave API", + "app_key": "Clave de aplicaci\u00f3n" + }, "title": "Completa tu informaci\u00f3n" } } diff --git a/homeassistant/components/ambient_station/.translations/fr.json b/homeassistant/components/ambient_station/.translations/fr.json index ede25d0bd4b7d0..b28cb374eacdfc 100644 --- a/homeassistant/components/ambient_station/.translations/fr.json +++ b/homeassistant/components/ambient_station/.translations/fr.json @@ -13,6 +13,7 @@ }, "title": "Veuillez saisir vos informations" } - } + }, + "title": "Ambient PWS" } } \ No newline at end of file diff --git a/homeassistant/components/ambient_station/.translations/ko.json b/homeassistant/components/ambient_station/.translations/ko.json index c316741d36b8ec..541b8699dc815f 100644 --- a/homeassistant/components/ambient_station/.translations/ko.json +++ b/homeassistant/components/ambient_station/.translations/ko.json @@ -11,7 +11,7 @@ "api_key": "API \ud0a4", "app_key": "Application \ud0a4" }, - "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694" + "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc785\ub825" } }, "title": "Ambient PWS" diff --git a/homeassistant/components/ambient_station/.translations/th.json b/homeassistant/components/ambient_station/.translations/th.json index 9f08ed5f1a2395..a6115413edcd85 100644 --- a/homeassistant/components/ambient_station/.translations/th.json +++ b/homeassistant/components/ambient_station/.translations/th.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "no_devices": "\u0e44\u0e21\u0e48\u0e1e\u0e1a\u0e2d\u0e38\u0e1b\u0e01\u0e23\u0e13\u0e4c\u0e43\u0e14\u0e46 \u0e43\u0e19\u0e1a\u0e31\u0e0d\u0e0a\u0e35\u0e40\u0e25\u0e22" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/auth/.translations/ko.json b/homeassistant/components/auth/.translations/ko.json index c5278c63f798c3..6c2e8988d83c58 100644 --- a/homeassistant/components/auth/.translations/ko.json +++ b/homeassistant/components/auth/.translations/ko.json @@ -26,7 +26,7 @@ "step": { "init": { "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574\uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google OTP](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", - "title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2\ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" + "title": "TOTP 2\ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" } }, "title": "TOTP (\uc2dc\uac04 \uae30\ubc18 OTP)" diff --git a/homeassistant/components/axis/.translations/es.json b/homeassistant/components/axis/.translations/es.json new file mode 100644 index 00000000000000..9229b90866fd3c --- /dev/null +++ b/homeassistant/components/axis/.translations/es.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "bad_config_file": "Datos err\u00f3neos en el archivo de configuraci\u00f3n", + "link_local_address": "Las direcciones de enlace locales no son compatibles" + }, + "error": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "device_unavailable": "El dispositivo no est\u00e1 disponible", + "faulty_credentials": "Credenciales de usuario incorrectas" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Contrase\u00f1a", + "port": "Puerto", + "username": "Nombre de usuario" + }, + "title": "Configurar dispositivo Axis" + } + }, + "title": "Dispositivo Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/fr.json b/homeassistant/components/axis/.translations/fr.json new file mode 100644 index 00000000000000..020cd8f5946ed5 --- /dev/null +++ b/homeassistant/components/axis/.translations/fr.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "bad_config_file": "Mauvaises donn\u00e9es du fichier de configuration", + "link_local_address": "Les adresses locales ne sont pas prises en charge" + }, + "error": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "device_unavailable": "L'appareil n'est pas disponible", + "faulty_credentials": "Mauvaises informations d'identification de l'utilisateur" + }, + "step": { + "user": { + "data": { + "host": "H\u00f4te", + "password": "Mot de passe", + "port": "Port", + "username": "Nom d'utilisateur" + }, + "title": "Configurer l'appareil Axis" + } + }, + "title": "Appareil Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/it.json b/homeassistant/components/axis/.translations/it.json new file mode 100644 index 00000000000000..2141bf34942bc8 --- /dev/null +++ b/homeassistant/components/axis/.translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "bad_config_file": "Dati errati dal file di configurazione" + }, + "error": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "device_unavailable": "Il dispositivo non \u00e8 disponibile", + "faulty_credentials": "Credenziali utente non valide" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Porta", + "username": "Nome utente" + }, + "title": "Impostazione del dispositivo Axis" + } + }, + "title": "Dispositivo Axis" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/ko.json b/homeassistant/components/axis/.translations/ko.json index f1543afbae8a39..aafa4fc18962e1 100644 --- a/homeassistant/components/axis/.translations/ko.json +++ b/homeassistant/components/axis/.translations/ko.json @@ -1,13 +1,13 @@ { "config": { "abort": { - "already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "bad_config_file": "\uad6c\uc131 \ud30c\uc77c\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "link_local_address": "\ub85c\uceec \uc8fc\uc18c \uc5f0\uacb0\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4" }, "error": { - "already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "device_unavailable": "\uc7a5\uce58\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "device_unavailable": "\uae30\uae30\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", "faulty_credentials": "\uc0ac\uc6a9\uc790 \uc774\ub984 \ud639\uc740 \ube44\ubc00\ubc88\ud638\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { @@ -18,9 +18,9 @@ "port": "\ud3ec\ud2b8", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" }, - "title": "Axis \uc7a5\uce58 \uc124\uc815" + "title": "Axis \uae30\uae30 \uc124\uc815" } }, - "title": "Axis \uc7a5\uce58" + "title": "Axis \uae30\uae30" } } \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/pt.json b/homeassistant/components/axis/.translations/pt.json index e71b890506da3a..77ce7025f70c98 100644 --- a/homeassistant/components/axis/.translations/pt.json +++ b/homeassistant/components/axis/.translations/pt.json @@ -3,8 +3,10 @@ "step": { "user": { "data": { + "host": "Servidor", "password": "Palavra-passe", - "port": "Porta" + "port": "Porta", + "username": "Nome de Utilizador" } } } diff --git a/homeassistant/components/axis/.translations/sv.json b/homeassistant/components/axis/.translations/sv.json new file mode 100644 index 00000000000000..435a56632e82e1 --- /dev/null +++ b/homeassistant/components/axis/.translations/sv.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten \u00e4r redan konfigurerad", + "bad_config_file": "Felaktig data fr\u00e5n config fil" + }, + "error": { + "already_configured": "Enheten \u00e4r redan konfigurerad", + "device_unavailable": "Enheten \u00e4r inte tillg\u00e4nglig", + "faulty_credentials": "Felaktiga anv\u00e4ndaruppgifter" + }, + "step": { + "user": { + "data": { + "host": "V\u00e4rd", + "password": "L\u00f6senord", + "port": "Port", + "username": "Anv\u00e4ndarnamn" + }, + "title": "Konfigurera Axis enhet" + } + }, + "title": "Axis enhet" + } +} \ No newline at end of file diff --git a/homeassistant/components/axis/.translations/th.json b/homeassistant/components/axis/.translations/th.json new file mode 100644 index 00000000000000..4226d4ddb3e5b6 --- /dev/null +++ b/homeassistant/components/axis/.translations/th.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19", + "port": "Port", + "username": "\u0e0a\u0e37\u0e48\u0e2d\u0e1c\u0e39\u0e49\u0e43\u0e0a\u0e49" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/.translations/ko.json b/homeassistant/components/cast/.translations/ko.json index e4472c88cd8e3a..32c744c8f20b0e 100644 --- a/homeassistant/components/cast/.translations/ko.json +++ b/homeassistant/components/cast/.translations/ko.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Googgle Cast \uc7a5\uce58\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_devices_found": "Googgle Cast \uae30\uae30\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\ud558\ub098\uc758 Google Cast \ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { diff --git a/homeassistant/components/cast/.translations/th.json b/homeassistant/components/cast/.translations/th.json index f0b06a06dc9a36..372a9cf0760dfa 100644 --- a/homeassistant/components/cast/.translations/th.json +++ b/homeassistant/components/cast/.translations/th.json @@ -1,9 +1,14 @@ { "config": { + "abort": { + "no_devices_found": "\u0e44\u0e21\u0e48\u0e1e\u0e1a\u0e2d\u0e38\u0e1b\u0e01\u0e23\u0e13\u0e4c Google Cast \u0e1a\u0e19\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22" + }, "step": { "confirm": { - "description": "\u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 Google Cast \u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48?" + "description": "\u0e04\u0e38\u0e13\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 Google Cast \u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48?", + "title": "Google Cast" } - } + }, + "title": "Google Cast" } } \ No newline at end of file diff --git a/homeassistant/components/daikin/.translations/es.json b/homeassistant/components/daikin/.translations/es.json new file mode 100644 index 00000000000000..d3a733a3f9be12 --- /dev/null +++ b/homeassistant/components/daikin/.translations/es.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "device_fail": "Error inesperado al crear el dispositivo.", + "device_timeout": "Tiempo de espera agotado al conectar con el dispositivo." + }, + "step": { + "user": { + "data": { + "host": "Host" + }, + "description": "Introduce la IP de tu aire acondicionado Daikin", + "title": "Configurar aire acondicionado Daikin" + } + }, + "title": "Aire acondicionado Daikin" + } +} \ No newline at end of file diff --git a/homeassistant/components/daikin/.translations/ko.json b/homeassistant/components/daikin/.translations/ko.json index a203affdbb8bb2..2291d46800d849 100644 --- a/homeassistant/components/daikin/.translations/ko.json +++ b/homeassistant/components/daikin/.translations/ko.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "device_fail": "\uc7a5\uce58\ub97c \uad6c\uc131\ud558\ub294\uc911 \uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", - "device_timeout": "\uc7a5\uce58 \uc5f0\uacb0 \uc2dc\uac04\uc774 \ucd08\uacfc\ud588\uc2b5\ub2c8\ub2e4." + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "device_fail": "\uae30\uae30\ub97c \uad6c\uc131\ud558\ub294 \ub3c4\uc911 \uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", + "device_timeout": "\uae30\uae30 \uc5f0\uacb0 \uc2dc\uac04\uc774 \ucd08\uacfc\ud588\uc2b5\ub2c8\ub2e4." }, "step": { "user": { diff --git a/homeassistant/components/deconz/.translations/ca.json b/homeassistant/components/deconz/.translations/ca.json index 87189a938064a8..8fb309f1a32dc3 100644 --- a/homeassistant/components/deconz/.translations/ca.json +++ b/homeassistant/components/deconz/.translations/ca.json @@ -9,6 +9,14 @@ "no_key": "No s'ha pogut obtenir una clau API" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Permet la importaci\u00f3 de sensors virtuals", + "allow_deconz_groups": "Permet la importaci\u00f3 de grups deCONZ" + }, + "description": "Vols configurar Home Assistant per a que es connecti amb la passarel\u00b7la deCONZ proporcionada per l\u2019add-on {addon} de hass.io?", + "title": "Passarel\u00b7la d'enlla\u00e7 deCONZ Zigbee (complement de Hass.io)" + }, "init": { "data": { "host": "Amfitri\u00f3", diff --git a/homeassistant/components/deconz/.translations/de.json b/homeassistant/components/deconz/.translations/de.json index 39975eaa39e350..04220dcb9fc3f1 100644 --- a/homeassistant/components/deconz/.translations/de.json +++ b/homeassistant/components/deconz/.translations/de.json @@ -9,6 +9,13 @@ "no_key": "Es konnte kein API-Schl\u00fcssel abgerufen werden" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Import virtueller Sensoren zulassen", + "allow_deconz_groups": "Import von deCONZ-Gruppen zulassen" + }, + "description": "M\u00f6chtest du Home Assistant so konfigurieren, dass er eine Verbindung mit dem deCONZ gateway herstellt, der vom Add-on hass.io {addon} bereitgestellt wird?" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/en.json b/homeassistant/components/deconz/.translations/en.json index d8bcc95a115a34..cfe781b6c1c935 100644 --- a/homeassistant/components/deconz/.translations/en.json +++ b/homeassistant/components/deconz/.translations/en.json @@ -9,6 +9,14 @@ "no_key": "Couldn't get an API key" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Allow importing virtual sensors", + "allow_deconz_groups": "Allow importing deCONZ groups" + }, + "description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the hass.io add-on {addon}?", + "title": "deCONZ Zigbee gateway via Hass.io add-on" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/es.json b/homeassistant/components/deconz/.translations/es.json index 34661f447d8181..6e23dcf74fd339 100644 --- a/homeassistant/components/deconz/.translations/es.json +++ b/homeassistant/components/deconz/.translations/es.json @@ -9,6 +9,14 @@ "no_key": "No se pudo obtener una clave API" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Permitir importar sensores virtuales", + "allow_deconz_groups": "Permite importar grupos de deCONZ" + }, + "description": "\u00bfQuieres configurar Home Assistant para que se conecte al gateway de deCONZ proporcionado por el add-on {addon} de hass.io?", + "title": "Add-on deCONZ Zigbee v\u00eda Hass.io" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/it.json b/homeassistant/components/deconz/.translations/it.json index c0a23d47be366d..dfff5743df7aa2 100644 --- a/homeassistant/components/deconz/.translations/it.json +++ b/homeassistant/components/deconz/.translations/it.json @@ -9,6 +9,14 @@ "no_key": "Impossibile ottenere una API key" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Consenti l'importazione di sensori virtuali", + "allow_deconz_groups": "Consenti l'importazione di gruppi deCONZ" + }, + "description": "Vuoi configurare Home Assistant per connettersi al gateway deCONZ fornito dal componente aggiuntivo hass.io {addon} ?", + "title": "Gateway Zigbee deCONZ tramite l'add-on Hass.io" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/ko.json b/homeassistant/components/deconz/.translations/ko.json index 6a527ab0a0b113..a2fa5955d7aaa9 100644 --- a/homeassistant/components/deconz/.translations/ko.json +++ b/homeassistant/components/deconz/.translations/ko.json @@ -9,6 +9,14 @@ "no_key": "API \ud0a4\ub97c \uac00\uc838\uc62c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "\uac00\uc0c1 \uc13c\uc11c \uac00\uc838\uc624\uae30 \ud5c8\uc6a9", + "allow_deconz_groups": "deCONZ \uadf8\ub8f9 \uac00\uc838\uc624\uae30 \ud5c8\uc6a9" + }, + "description": "Hass.io \ubd80\uac00\uae30\ub2a5 {addon} \ub85c(\uc73c\ub85c) deCONZ \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud558\ub3c4\ub85d Home Assistant \ub97c \uad6c\uc131 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", + "title": "Hass.io \uc560\ub4dc\uc628\uc758 deCONZ Zigbee \uac8c\uc774\ud2b8\uc6e8\uc774" + }, "init": { "data": { "host": "\ud638\uc2a4\ud2b8", @@ -25,7 +33,7 @@ "allow_clip_sensor": "\uac00\uc0c1 \uc13c\uc11c \uac00\uc838\uc624\uae30 \ud5c8\uc6a9", "allow_deconz_groups": "deCONZ \uadf8\ub8f9 \uac00\uc838\uc624\uae30 \ud5c8\uc6a9" }, - "title": "deCONZ\ub97c \uc704\ud55c \ucd94\uac00 \uad6c\uc131 \uc635\uc158" + "title": "deCONZ \ucd94\uac00 \uad6c\uc131 \uc635\uc158" } }, "title": "deCONZ Zigbee \uac8c\uc774\ud2b8\uc6e8\uc774" diff --git a/homeassistant/components/deconz/.translations/pl.json b/homeassistant/components/deconz/.translations/pl.json index 5a8b710c006d96..022a3284c14fb2 100644 --- a/homeassistant/components/deconz/.translations/pl.json +++ b/homeassistant/components/deconz/.translations/pl.json @@ -9,6 +9,14 @@ "no_key": "Nie mo\u017cna uzyska\u0107 klucza API" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Zezwalaj na importowanie wirtualnych sensor\u00f3w", + "allow_deconz_groups": "Zezw\u00f3l na importowanie grup deCONZ" + }, + "description": "Czy chcesz skonfigurowa\u0107 Home Assistant'a, aby po\u0142\u0105czy\u0142 si\u0119 z bramk\u0105 deCONZ dostarczon\u0105 przez dodatek hass.io {addon}?", + "title": "Bramka deCONZ Zigbee przez dodatek Hass.io" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/ru.json b/homeassistant/components/deconz/.translations/ru.json index 5fd31ab9d8f2c0..61488e5ec9af5e 100644 --- a/homeassistant/components/deconz/.translations/ru.json +++ b/homeassistant/components/deconz/.translations/ru.json @@ -9,6 +9,14 @@ "no_key": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432", + "allow_deconz_groups": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442 \u0433\u0440\u0443\u043f\u043f deCONZ" + }, + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a deCONZ (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Hass.io \"{addon}\")?", + "title": "Zigbee \u0448\u043b\u044e\u0437 deCONZ (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Hass.io)" + }, "init": { "data": { "host": "\u0425\u043e\u0441\u0442", diff --git a/homeassistant/components/deconz/.translations/sl.json b/homeassistant/components/deconz/.translations/sl.json index 686bb5b1e2eb61..ae9329c2857ff4 100644 --- a/homeassistant/components/deconz/.translations/sl.json +++ b/homeassistant/components/deconz/.translations/sl.json @@ -9,6 +9,14 @@ "no_key": "Klju\u010da API ni mogo\u010de dobiti" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Dovoli uvoz virtualnih senzorjev", + "allow_deconz_groups": "Dovoli uvoz deCONZ skupin" + }, + "description": "\u017delite konfigurirati Home Assistant-a za povezavo z deCONZ prehodom, ki ga ponuja hass.io dodatek {addon} ?", + "title": "deCONZ Zigbee prehod preko dodatka Hass.io" + }, "init": { "data": { "host": "Gostitelj", diff --git a/homeassistant/components/deconz/.translations/zh-Hant.json b/homeassistant/components/deconz/.translations/zh-Hant.json index 524f68d41bc057..a31e5ba4a078d6 100644 --- a/homeassistant/components/deconz/.translations/zh-Hant.json +++ b/homeassistant/components/deconz/.translations/zh-Hant.json @@ -9,6 +9,14 @@ "no_key": "\u7121\u6cd5\u53d6\u5f97 API key" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "\u5141\u8a31\u532f\u5165\u865b\u64ec\u611f\u61c9\u5668", + "allow_deconz_groups": "\u5141\u8a31\u532f\u5165 deCONZ \u7fa4\u7d44" + }, + "description": "\u662f\u5426\u8981\u8a2d\u5b9a Home Assistant \u4ee5\u9023\u7dda\u81f3 Hass.io \u9644\u52a0\u7d44\u4ef6 {addon} \u4e4b deCONZ \u9598\u9053\u5668\uff1f", + "title": "\u900f\u904e Hass.io \u9644\u52a0\u7d44\u4ef6 deCONZ Zigbee \u9598\u9053\u5668" + }, "init": { "data": { "host": "\u4e3b\u6a5f\u7aef", diff --git a/homeassistant/components/dialogflow/.translations/fr.json b/homeassistant/components/dialogflow/.translations/fr.json new file mode 100644 index 00000000000000..53edb21b8e82c3 --- /dev/null +++ b/homeassistant/components/dialogflow/.translations/fr.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "not_internet_accessible": "Votre instance de Home Assistant doit \u00eatre accessible depuis Internet pour recevoir les messages Dialogflow.", + "one_instance_allowed": "Une seule instance est n\u00e9cessaire." + }, + "create_entry": { + "default": "Pour envoyer des \u00e9v\u00e9nements \u00e0 Home Assistant, vous devez configurer [Webhooks avec Mailgun] ( {mailgun_url} ). \n\n Remplissez les informations suivantes: \n\n - URL: ` {webhook_url} ` \n - M\u00e9thode: POST \n - Type de contenu: application / json \n\n Voir [la documentation] ( {docs_url} ) pour savoir comment configurer les automatisations pour g\u00e9rer les donn\u00e9es entrantes." + }, + "step": { + "user": { + "description": "\u00cates-vous s\u00fbr de vouloir configurer Dialogflow?", + "title": "Configurer le Webhook Dialogflow" + } + }, + "title": "Dialogflow" + } +} \ No newline at end of file diff --git a/homeassistant/components/dialogflow/.translations/ru.json b/homeassistant/components/dialogflow/.translations/ru.json index 899f776c095fd3..d8b7db09a78c62 100644 --- a/homeassistant/components/dialogflow/.translations/ru.json +++ b/homeassistant/components/dialogflow/.translations/ru.json @@ -10,7 +10,7 @@ "step": { "user": { "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Dialogflow?", - "title": "Dialogflow Webhook" + "title": "Dialogflow" } }, "title": "Dialogflow" diff --git a/homeassistant/components/ebusd/.translations/th.json b/homeassistant/components/ebusd/.translations/th.json index 92a8c7969a8d85..0f12574d8b96a1 100644 --- a/homeassistant/components/ebusd/.translations/th.json +++ b/homeassistant/components/ebusd/.translations/th.json @@ -1,5 +1,6 @@ { "state": { + "day": "\u0e27\u0e31\u0e19", "night": "\u0e01\u0e25\u0e32\u0e07\u0e04\u0e37\u0e19" } } \ No newline at end of file diff --git a/homeassistant/components/esphome/.translations/es.json b/homeassistant/components/esphome/.translations/es.json index c4b18899eafbf2..ea79edc0b31ea9 100644 --- a/homeassistant/components/esphome/.translations/es.json +++ b/homeassistant/components/esphome/.translations/es.json @@ -14,6 +14,10 @@ "description": "Escribe la contrase\u00f1a que hayas establecido en tu configuraci\u00f3n.", "title": "Escribe la contrase\u00f1a" }, + "discovery_confirm": { + "description": "\u00bfQuieres a\u00f1adir el nodo `{name}` de ESPHome a Home Assistant?", + "title": "Nodo ESPHome descubierto" + }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/hangouts/.translations/ca.json b/homeassistant/components/hangouts/.translations/ca.json index ca15e59ec194e3..ea43c804f2d1af 100644 --- a/homeassistant/components/hangouts/.translations/ca.json +++ b/homeassistant/components/hangouts/.translations/ca.json @@ -18,6 +18,7 @@ }, "user": { "data": { + "authorization_code": "Codi d'autoritzaci\u00f3 (necessari per a l'autenticaci\u00f3 manual)", "email": "Correu electr\u00f2nic", "password": "Contrasenya" }, diff --git a/homeassistant/components/hangouts/.translations/de.json b/homeassistant/components/hangouts/.translations/de.json index c8e84983fb615c..fa96c00f666cd3 100644 --- a/homeassistant/components/hangouts/.translations/de.json +++ b/homeassistant/components/hangouts/.translations/de.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "Autorisierungscode (f\u00fcr die manuelle Authentifizierung erforderlich)", "email": "E-Mail-Adresse", "password": "Passwort" }, diff --git a/homeassistant/components/hangouts/.translations/es.json b/homeassistant/components/hangouts/.translations/es.json index 4b7ad390ceb824..01200a3aef9fa9 100644 --- a/homeassistant/components/hangouts/.translations/es.json +++ b/homeassistant/components/hangouts/.translations/es.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "C\u00f3digo de autorizaci\u00f3n (requerido para la autenticaci\u00f3n manual)", "email": "Correo electr\u00f3nico", "password": "Contrase\u00f1a" }, diff --git a/homeassistant/components/hangouts/.translations/ko.json b/homeassistant/components/hangouts/.translations/ko.json index b1bcf5725bef6c..e045f3359d1542 100644 --- a/homeassistant/components/hangouts/.translations/ko.json +++ b/homeassistant/components/hangouts/.translations/ko.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "\uc778\uc99d \ucf54\ub4dc (\uc218\ub3d9 \uc778\uc99d\uc5d0 \ud544\uc694)", "email": "\uc774\uba54\uc77c \uc8fc\uc18c", "password": "\ube44\ubc00\ubc88\ud638" }, diff --git a/homeassistant/components/hangouts/.translations/pl.json b/homeassistant/components/hangouts/.translations/pl.json index 5e0ecfa2900602..5da1e21979970c 100644 --- a/homeassistant/components/hangouts/.translations/pl.json +++ b/homeassistant/components/hangouts/.translations/pl.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "Kod autoryzacji (wymagany do r\u0119cznego uwierzytelnienia)", "email": "Adres e-mail", "password": "Has\u0142o" }, diff --git a/homeassistant/components/hangouts/.translations/ru.json b/homeassistant/components/hangouts/.translations/ru.json index 143d6bc88ba111..52b8798c0f4084 100644 --- a/homeassistant/components/hangouts/.translations/ru.json +++ b/homeassistant/components/hangouts/.translations/ru.json @@ -18,10 +18,11 @@ }, "user": { "data": { + "authorization_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438)", "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b", "password": "\u041f\u0430\u0440\u043e\u043b\u044c" }, - "title": "\u0412\u0445\u043e\u0434 \u0432 Google Hangouts" + "title": "Google Hangouts" } }, "title": "Google Hangouts" diff --git a/homeassistant/components/hangouts/.translations/sl.json b/homeassistant/components/hangouts/.translations/sl.json index db85f338b67d35..64ca6da10acd3a 100644 --- a/homeassistant/components/hangouts/.translations/sl.json +++ b/homeassistant/components/hangouts/.translations/sl.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "Koda pooblastila (potrebna za ro\u010dno overjanje)", "email": "E-po\u0161tni naslov", "password": "Geslo" }, diff --git a/homeassistant/components/hangouts/.translations/th.json b/homeassistant/components/hangouts/.translations/th.json index 7b6645edca227a..bcc59392e2e9e8 100644 --- a/homeassistant/components/hangouts/.translations/th.json +++ b/homeassistant/components/hangouts/.translations/th.json @@ -6,6 +6,7 @@ }, "user": { "data": { + "email": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48\u0e2d\u0e35\u0e40\u0e21\u0e25", "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19" }, "description": "\u0e27\u0e48\u0e32\u0e07\u0e40\u0e1b\u0e25\u0e48\u0e32" diff --git a/homeassistant/components/hangouts/.translations/zh-Hant.json b/homeassistant/components/hangouts/.translations/zh-Hant.json index 16234acb193e62..c8da604e6f2684 100644 --- a/homeassistant/components/hangouts/.translations/zh-Hant.json +++ b/homeassistant/components/hangouts/.translations/zh-Hant.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "\u9a57\u8b49\u78bc\uff08\u624b\u52d5\u9a57\u8b49\u5fc5\u9808\uff09", "email": "\u96fb\u5b50\u90f5\u4ef6", "password": "\u5bc6\u78bc" }, diff --git a/homeassistant/components/heos/.translations/de.json b/homeassistant/components/heos/.translations/de.json new file mode 100644 index 00000000000000..0e2fa724e4112e --- /dev/null +++ b/homeassistant/components/heos/.translations/de.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Es kann nur eine einzige Heos-Verbindung konfiguriert werden, da diese alle Ger\u00e4te im Netzwerk unterst\u00fctzt." + }, + "error": { + "connection_failure": "Es kann keine Verbindung zum angegebenen Host hergestellt werden." + }, + "step": { + "user": { + "data": { + "access_token": "Host" + }, + "description": "Bitte gib den Hostnamen oder die IP-Adresse eines Heos-Ger\u00e4ts ein (vorzugsweise eines, das per Kabel mit dem Netzwerk verbunden ist).", + "title": "Mit Heos verbinden" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/es.json b/homeassistant/components/heos/.translations/es.json new file mode 100644 index 00000000000000..5a2dd096024220 --- /dev/null +++ b/homeassistant/components/heos/.translations/es.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Solo puedes configurar una \u00fanica conexi\u00f3n Heos, ya que admitir\u00e1 todos los dispositivos de la red." + }, + "error": { + "connection_failure": "No se puede conectar al host especificado." + }, + "step": { + "user": { + "data": { + "access_token": "Host" + }, + "description": "Introduce el nombre de host o direcci\u00f3n IP de un dispositivo Heos (preferiblemente conectado por cable a la red).", + "title": "Conectar a Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/fr.json b/homeassistant/components/heos/.translations/fr.json new file mode 100644 index 00000000000000..274075af7499b7 --- /dev/null +++ b/homeassistant/components/heos/.translations/fr.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Vous ne pouvez configurer qu'une seule connexion Heos, car celle-ci supportera tous les p\u00e9riph\u00e9riques du r\u00e9seau." + }, + "error": { + "connection_failure": "Impossible de se connecter \u00e0 l'h\u00f4te sp\u00e9cifi\u00e9." + }, + "step": { + "user": { + "data": { + "access_token": "H\u00f4te" + }, + "description": "Veuillez saisir le nom d\u2019h\u00f4te ou l\u2019adresse IP d\u2019un p\u00e9riph\u00e9rique Heos (de pr\u00e9f\u00e9rence connect\u00e9 au r\u00e9seau filaire).", + "title": "Se connecter \u00e0 Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/it.json b/homeassistant/components/heos/.translations/it.json new file mode 100644 index 00000000000000..32667d0dbe8e41 --- /dev/null +++ b/homeassistant/components/heos/.translations/it.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "connection_failure": "Impossibile connettersi all'host specificato." + }, + "step": { + "user": { + "data": { + "access_token": "Host" + }, + "description": "Inserire il nome host o l'indirizzo IP di un dispositivo Heos (preferibilmente uno connesso alla rete tramite cavo).", + "title": "Connetti a Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/ko.json b/homeassistant/components/heos/.translations/ko.json new file mode 100644 index 00000000000000..df565ee889fde7 --- /dev/null +++ b/homeassistant/components/heos/.translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Heos \uc5f0\uacb0\uc740 \ub124\ud2b8\uc6cc\ud06c\uc0c1\uc758 \ubaa8\ub4e0 \uae30\uae30\ub97c \uc9c0\uc6d0\ud558\uae30 \ub54c\ubb38\uc5d0 \ud558\ub098\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "connection_failure": "\uc9c0\uc815\ub41c \ud638\uc2a4\ud2b8\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + }, + "step": { + "user": { + "data": { + "access_token": "\ud638\uc2a4\ud2b8" + }, + "description": "Heos \uae30\uae30\uc758 \ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. (\uc720\uc120 \ub124\ud2b8\uc6cc\ud06c\ub85c \uc5f0\uacb0\ud558\ub294 \uac83\uc774 \uc88b\uc2b5\ub2c8\ub2e4)", + "title": "Heos \uc5f0\uacb0" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/no.json b/homeassistant/components/heos/.translations/no.json index 12ed8cc457a5d4..f7595d80b96df3 100644 --- a/homeassistant/components/heos/.translations/no.json +++ b/homeassistant/components/heos/.translations/no.json @@ -1,5 +1,20 @@ { "config": { + "abort": { + "already_setup": "Du kan kun konfigurere en enkelt Heos tilkobling, da den st\u00f8tter alle enhetene p\u00e5 nettverket." + }, + "error": { + "connection_failure": "Kan ikke koble til den angitte verten." + }, + "step": { + "user": { + "data": { + "access_token": "Vert" + }, + "description": "Vennligst skriv inn vertsnavnet eller IP-adressen til en Heos-enhet (helst en tilkoblet via kabel til nettverket).", + "title": "Koble til Heos" + } + }, "title": "Heos" } } \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/pl.json b/homeassistant/components/heos/.translations/pl.json new file mode 100644 index 00000000000000..faa104d20eab1b --- /dev/null +++ b/homeassistant/components/heos/.translations/pl.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Mo\u017cesz skonfigurowa\u0107 tylko jedno po\u0142\u0105czenie Heos, poniewa\u017c b\u0119dzie ono obs\u0142ugiwa\u0107 wszystkie urz\u0105dzenia w sieci." + }, + "error": { + "connection_failure": "Nie mo\u017cna po\u0142\u0105czy\u0107 si\u0119 z okre\u015blonym hostem." + }, + "step": { + "user": { + "data": { + "access_token": "Host" + }, + "description": "Wprowad\u017a nazw\u0119 hosta lub adres IP urz\u0105dzenia Heos (preferowane po\u0142\u0105czenie kablowe, nie WiFi).", + "title": "Po\u0142\u0105cz si\u0119 z Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/pt.json b/homeassistant/components/heos/.translations/pt.json new file mode 100644 index 00000000000000..33c83fdc738af5 --- /dev/null +++ b/homeassistant/components/heos/.translations/pt.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "access_token": "Servidor" + } + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/sl.json b/homeassistant/components/heos/.translations/sl.json new file mode 100644 index 00000000000000..1e84381e7a65d2 --- /dev/null +++ b/homeassistant/components/heos/.translations/sl.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Konfigurirate lahko samo eno povezavo Heos, le ta bo podpirala vse naprave v omre\u017eju." + }, + "error": { + "connection_failure": "Ni mogo\u010de vzpostaviti povezave z dolo\u010denim gostiteljem." + }, + "step": { + "user": { + "data": { + "access_token": "Gostitelj" + }, + "description": "Vnesite ime gostitelja ali naslov IP naprave Heos (po mo\u017enosti eno, ki je z omre\u017ejem povezana \u017ei\u010dno).", + "title": "Pove\u017eite se z Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/sv.json b/homeassistant/components/heos/.translations/sv.json new file mode 100644 index 00000000000000..6e5d825ef26074 --- /dev/null +++ b/homeassistant/components/heos/.translations/sv.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "connection_failure": "Det gick inte att ansluta till den angivna v\u00e4rden." + }, + "step": { + "user": { + "data": { + "access_token": "V\u00e4rd" + }, + "description": "Ange v\u00e4rdnamnet eller IP-adressen f\u00f6r en Heos-enhet (helst en ansluten via kabel till n\u00e4tverket).", + "title": "Anslut till Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/zh-Hant.json b/homeassistant/components/heos/.translations/zh-Hant.json new file mode 100644 index 00000000000000..87950f41b65ba1 --- /dev/null +++ b/homeassistant/components/heos/.translations/zh-Hant.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44 Heos \u9023\u7dda\uff0c\u5c07\u652f\u63f4\u7db2\u8def\u4e2d\u6240\u6709\u5c0d\u61c9\u88dd\u7f6e\u3002" + }, + "error": { + "connection_failure": "\u7121\u6cd5\u9023\u7dda\u81f3\u6307\u5b9a\u4e3b\u6a5f\u7aef\u3002" + }, + "step": { + "user": { + "data": { + "access_token": "\u4e3b\u6a5f\u7aef" + }, + "description": "\u8acb\u8f38\u5165\u4e3b\u6a5f\u6bb5\u540d\u7a31\u6216 Heos \u88dd\u7f6e IP \u4f4d\u5740\uff08\u5df2\u900f\u904e\u6709\u7dda\u7db2\u8def\u9023\u7dda\uff09\u3002", + "title": "\u9023\u7dda\u81f3 Heos" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/it.json b/homeassistant/components/homekit_controller/.translations/it.json new file mode 100644 index 00000000000000..6ec1c28344845c --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/it.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "L'accessorio \u00e8 gi\u00e0 configurato con questo controller." + }, + "error": { + "authentication_error": "Codice HomeKit errato. Per favore, controllate e riprovate.", + "unable_to_pair": "Impossibile abbinare, per favore riprova.", + "unknown_error": "Il dispositivo ha riportato un errore sconosciuto. L'abbinamento non \u00e8 riuscito." + }, + "step": { + "pair": { + "data": { + "pairing_code": "Codice di abbinamento" + }, + "description": "Inserisci il codice di abbinamento HomeKit per usare questo accessorio", + "title": "Abbina con accessorio HomeKit" + }, + "user": { + "data": { + "device": "Dispositivo" + }, + "description": "Selezionare il dispositivo che si desidera abbinare", + "title": "Abbina con accessorio HomeKit" + } + }, + "title": "Accessorio HomeKit" + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/ko.json b/homeassistant/components/homekit_controller/.translations/ko.json index 525604cd96bee9..b512ad677328cf 100644 --- a/homeassistant/components/homekit_controller/.translations/ko.json +++ b/homeassistant/components/homekit_controller/.translations/ko.json @@ -4,8 +4,8 @@ "already_configured": "\uc561\uc138\uc11c\ub9ac\uac00 \ucee8\ud2b8\ub864\ub7ec\uc5d0 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "already_paired": "\uc774 \uc561\uc138\uc11c\ub9ac\ub294 \uc774\ubbf8 \ub2e4\ub978 \uae30\uae30\uc640 \ud398\uc5b4\ub9c1\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4. \uc561\uc138\uc11c\ub9ac\ub97c \uc7ac\uc124\uc815\ud558\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", "ignored_model": "\uc774 \ubaa8\ub378\uc5d0 \ub300\ud55c HomeKit \uc9c0\uc6d0\uc740 \ub354 \ub9ce\uc740 \uae30\ub2a5\uc744 \uc81c\uacf5\ud558\ub294 \uae30\ubcf8 \uad6c\uc131\uc694\uc18c\ub85c \uc778\ud574 \ucc28\ub2e8\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", - "invalid_config_entry": "\uc774 \uc7a5\uce58\ub294 \ud398\uc5b4\ub9c1 \ud560 \uc900\ube44\uac00 \ub418\uc5c8\uc9c0\ub9cc Home Assistant \uc5d0 \uc774\ubbf8 \uad6c\uc131\ub418\uc5b4 \ucda9\ub3cc\ud558\ub294 \uad6c\uc131\uc694\uc18c\uac00 \uc788\uc2b5\ub2c8\ub2e4. \uba3c\uc800 \ud574\ub2f9 \uad6c\uc131\uc694\uc18c\ub97c \uc81c\uac70\ud574\uc8fc\uc138\uc694.", - "no_devices": "\ud398\uc5b4\ub9c1\ub418\uc9c0 \uc54a\uc740 \uc7a5\uce58\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + "invalid_config_entry": "\uc774 \uae30\uae30\ub294 \ud398\uc5b4\ub9c1 \ud560 \uc900\ube44\uac00 \ub418\uc5c8\uc9c0\ub9cc Home Assistant \uc5d0 \uc774\ubbf8 \uad6c\uc131\ub418\uc5b4 \ucda9\ub3cc\ud558\ub294 \uad6c\uc131\uc694\uc18c\uac00 \uc788\uc2b5\ub2c8\ub2e4. \uba3c\uc800 \ud574\ub2f9 \uad6c\uc131\uc694\uc18c\ub97c \uc81c\uac70\ud574\uc8fc\uc138\uc694.", + "no_devices": "\ud398\uc5b4\ub9c1\ub418\uc9c0 \uc54a\uc740 \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" }, "error": { "authentication_error": "HomeKit \ucf54\ub4dc\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud655\uc778 \ud6c4 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", @@ -22,9 +22,9 @@ }, "user": { "data": { - "device": "\uc7a5\uce58" + "device": "\uae30\uae30" }, - "description": "\ud398\uc5b4\ub9c1 \ud560 \uc7a5\uce58\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694", + "description": "\ud398\uc5b4\ub9c1 \ud560 \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694", "title": "HomeKit \uc561\uc138\uc11c\ub9ac \ud398\uc5b4\ub9c1" } }, diff --git a/homeassistant/components/homekit_controller/.translations/pt.json b/homeassistant/components/homekit_controller/.translations/pt.json new file mode 100644 index 00000000000000..37f68408ce44fa --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/pt.json @@ -0,0 +1,16 @@ +{ + "config": { + "step": { + "pair": { + "data": { + "pairing_code": "C\u00f3digo de emparelhamento" + } + }, + "user": { + "data": { + "device": "Dispositivo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/sv.json b/homeassistant/components/homekit_controller/.translations/sv.json new file mode 100644 index 00000000000000..d1453b649382eb --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/sv.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "unable_to_pair": "Det g\u00e5r inte att para ihop, f\u00f6rs\u00f6k igen." + }, + "step": { + "pair": { + "title": "Para HomeKit-tillbeh\u00f6r" + }, + "user": { + "data": { + "device": "Enhet" + }, + "description": "V\u00e4lj den enhet du vill para med", + "title": "Para HomeKit-tillbeh\u00f6r" + } + }, + "title": "HomeKit-tillbeh\u00f6r" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/ko.json b/homeassistant/components/homematicip_cloud/.translations/ko.json index b60da944f648cc..2f47fcddf28ff4 100644 --- a/homeassistant/components/homematicip_cloud/.translations/ko.json +++ b/homeassistant/components/homematicip_cloud/.translations/ko.json @@ -15,14 +15,14 @@ "init": { "data": { "hapid": "\uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8 ID (SGTIN)", - "name": "\uc774\ub984 (\uc120\ud0dd \uc0ac\ud56d, \ubaa8\ub4e0 \uc7a5\uce58 \uc774\ub984\uc758 \uc811\ub450\uc5b4\ub85c \uc0ac\uc6a9)", + "name": "\uc774\ub984 (\uc120\ud0dd \uc0ac\ud56d, \ubaa8\ub4e0 \uae30\uae30 \uc774\ub984\uc758 \uc811\ub450\uc5b4\ub85c \uc0ac\uc6a9)", "pin": "PIN \ucf54\ub4dc (\uc120\ud0dd\uc0ac\ud56d)" }, "title": "HomematicIP \uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8 \uc120\ud0dd" }, "link": { "description": "Home Assistant \uc5d0 HomematicIP \ub97c \ub4f1\ub85d\ud558\ub824\uba74 \uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8\uc758 \ud30c\ub780\uc0c9 \ubc84\ud2bc\uacfc Submit \ubc84\ud2bc\uc744 \ub20c\ub7ec\uc8fc\uc138\uc694.\n\n![\ube0c\ub9bf\uc9c0\uc758 \ubc84\ud2bc \uc704\uce58 \ubcf4\uae30](/static/images/config_flows/config_homematicip_cloud.png)", - "title": "\uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8\uc5d0 \uc5f0\uacb0" + "title": "\uc561\uc138\uc2a4 \ud3ec\uc778\ud2b8 \uc5f0\uacb0" } }, "title": "HomematicIP \ud074\ub77c\uc6b0\ub4dc" diff --git a/homeassistant/components/hue/.translations/th.json b/homeassistant/components/hue/.translations/th.json index 0b91783f804390..c76064c0ab602a 100644 --- a/homeassistant/components/hue/.translations/th.json +++ b/homeassistant/components/hue/.translations/th.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "unknown": "\u0e40\u0e01\u0e34\u0e14\u0e02\u0e49\u0e2d\u0e1c\u0e34\u0e14\u0e1e\u0e25\u0e32\u0e14\u0e17\u0e35\u0e48\u0e44\u0e21\u0e48\u0e17\u0e23\u0e32\u0e1a\u0e2a\u0e32\u0e40\u0e2b\u0e15\u0e38" + }, + "error": { + "register_failed": "\u0e01\u0e32\u0e23\u0e25\u0e07\u0e17\u0e30\u0e40\u0e1a\u0e35\u0e22\u0e19\u0e25\u0e49\u0e21\u0e40\u0e2b\u0e25\u0e27\u0e42\u0e1b\u0e23\u0e14\u0e25\u0e2d\u0e07\u0e2d\u0e35\u0e01\u0e04\u0e23\u0e31\u0e49\u0e07" + }, "title": "Philips Hue" } } \ No newline at end of file diff --git a/homeassistant/components/ifttt/.translations/ru.json b/homeassistant/components/ifttt/.translations/ru.json index 4184d2dfadc151..ae5fdbab3f66c4 100644 --- a/homeassistant/components/ifttt/.translations/ru.json +++ b/homeassistant/components/ifttt/.translations/ru.json @@ -10,7 +10,7 @@ "step": { "user": { "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c IFTTT?", - "title": "IFTTT Webhook" + "title": "IFTTT" } }, "title": "IFTTT" diff --git a/homeassistant/components/ipma/.translations/fr.json b/homeassistant/components/ipma/.translations/fr.json index 058908db36ba4f..1ca5353ec7eb14 100644 --- a/homeassistant/components/ipma/.translations/fr.json +++ b/homeassistant/components/ipma/.translations/fr.json @@ -5,6 +5,11 @@ }, "step": { "user": { + "data": { + "latitude": "Latitude", + "longitude": "Longitude", + "name": "Nom" + }, "title": "Emplacement" } }, diff --git a/homeassistant/components/lifx/.translations/ko.json b/homeassistant/components/lifx/.translations/ko.json index c795c54badb5d1..2f3ec6db13d093 100644 --- a/homeassistant/components/lifx/.translations/ko.json +++ b/homeassistant/components/lifx/.translations/ko.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "LIFX \uc7a5\uce58\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_devices_found": "LIFX \uae30\uae30\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\ud558\ub098\uc758 LIFX \ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { diff --git a/homeassistant/components/locative/.translations/es.json b/homeassistant/components/locative/.translations/es.json new file mode 100644 index 00000000000000..d32250d51954a0 --- /dev/null +++ b/homeassistant/components/locative/.translations/es.json @@ -0,0 +1,14 @@ +{ + "config": { + "create_entry": { + "default": "Para enviar ubicaciones a Home Assistant, es necesario configurar la caracter\u00edstica webhook en la app de Locative.\n\nRellena la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nRevisa [la documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + }, + "step": { + "user": { + "description": "\u00bfEst\u00e1s seguro que quieres configurar el webhook de Locative?", + "title": "Configurar el webhook de Locative" + } + }, + "title": "Webhook de Locative" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/ca.json b/homeassistant/components/logi_circle/.translations/ca.json new file mode 100644 index 00000000000000..f3c201d19fc0d2 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/ca.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_setup": "Nom\u00e9s pots configurar un \u00fanic compte de Logi Circule.", + "external_error": "S'ha produ\u00eft una excepci\u00f3 d\u2019un altre flux de dades.", + "external_setup": "Logi Circle s'ha configurat correctament des d'un altre flux de dades.", + "no_flows": "Necessites configurar Logi Circle abans de poder autenticar-t'hi. Llegeix les [instruccions](https://www.home-assistant.io/components/logi_circle/)." + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa amb Logi Circle." + }, + "error": { + "auth_error": "Ha fallat l\u2019autoritzaci\u00f3 de l\u2019API.", + "auth_timeout": "L\u2019autoritzaci\u00f3 ha expirat durant l'obtenci\u00f3 del testimoni d\u2019acc\u00e9s.", + "follow_link": "V\u00e9s a l'enlla\u00e7 i autentica't abans de pr\u00e9mer Enviar" + }, + "step": { + "auth": { + "description": "V\u00e9s a l'enlla\u00e7 de sota i Accepta l'acc\u00e9s al teu compte de Logi Circle, despr\u00e9s, torna i prem Enviar (tamb\u00e9 a sota).\n\n[Enlla\u00e7]({authorization_url})", + "title": "Autenticaci\u00f3 amb Logi Circle" + }, + "user": { + "data": { + "flow_impl": "Prove\u00efdor" + }, + "description": "Tria quin prove\u00efdor d'autenticaci\u00f3 vols utilitzar per autenticar-te amb Logi Circle.", + "title": "Prove\u00efdor d'autenticaci\u00f3" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/en.json b/homeassistant/components/logi_circle/.translations/en.json index 57dd0b709b7102..bf3c059f81ab9b 100644 --- a/homeassistant/components/logi_circle/.translations/en.json +++ b/homeassistant/components/logi_circle/.translations/en.json @@ -1,32 +1,32 @@ { - "config": { - "title": "Logi Circle", - "step": { - "user": { - "title": "Authentication Provider", - "description": "Pick via which authentication provider you want to authenticate with Logi Circle.", - "data": { - "flow_impl": "Provider" - } - }, - "auth": { - "title": "Authenticate with Logi Circle", - "description": "Please follow the link below and Accept access to your Logi Circle account, then come back and press Submit below.\n\n[Link]({authorization_url})" - } - }, - "create_entry": { - "default": "Successfully authenticated with Logi Circle." - }, - "error": { - "auth_error": "API authorization failed.", - "auth_timeout": "Authorization timed out when requesting access token.", - "follow_link": "Please follow the link and authenticate before pressing Submit." - }, - "abort": { - "already_setup": "You can only configure a single Logi Circle account.", - "external_error": "Exception occurred from another flow.", - "external_setup": "Logi Circle successfully configured from another flow.", - "no_flows": "You need to configure Logi Circle before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/logi_circle/)." + "config": { + "abort": { + "already_setup": "You can only configure a single Logi Circle account.", + "external_error": "Exception occurred from another flow.", + "external_setup": "Logi Circle successfully configured from another flow.", + "no_flows": "You need to configure Logi Circle before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/logi_circle/)." + }, + "create_entry": { + "default": "Successfully authenticated with Logi Circle." + }, + "error": { + "auth_error": "API authorization failed.", + "auth_timeout": "Authorization timed out when requesting access token.", + "follow_link": "Please follow the link and authenticate before pressing Submit." + }, + "step": { + "auth": { + "description": "Please follow the link below and Accept access to your Logi Circle account, then come back and press Submit below.\n\n[Link]({authorization_url})", + "title": "Authenticate with Logi Circle" + }, + "user": { + "data": { + "flow_impl": "Provider" + }, + "description": "Pick via which authentication provider you want to authenticate with Logi Circle.", + "title": "Authentication Provider" + } + }, + "title": "Logi Circle" } - } -} +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/ru.json b/homeassistant/components/logi_circle/.translations/ru.json new file mode 100644 index 00000000000000..5e4d0890bfd3bf --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/ru.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "external_error": "\u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430.", + "external_setup": "Logi Circle \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430.", + "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Logi Circle \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/logi_circle/)." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "auth_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 API.", + "auth_timeout": "\u041f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", + "follow_link": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u044c \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\"." + }, + "step": { + "auth": { + "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e [\u0441\u0441\u044b\u043b\u043a\u0435]({authorization_url}) \u0438 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Logi Circle, \u0437\u0430\u0442\u0435\u043c \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430 \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c.", + "title": "Logi Circle" + }, + "user": { + "data": { + "flow_impl": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0432\u0445\u043e\u0434.", + "title": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/luftdaten/.translations/fr.json b/homeassistant/components/luftdaten/.translations/fr.json index 2aeb29fcf0ab32..3e1d41be34972e 100644 --- a/homeassistant/components/luftdaten/.translations/fr.json +++ b/homeassistant/components/luftdaten/.translations/fr.json @@ -8,7 +8,8 @@ "step": { "user": { "data": { - "show_on_map": "Montrer sur la carte" + "show_on_map": "Montrer sur la carte", + "station_id": "ID capteur Luftdaten" }, "title": "D\u00e9finir Luftdaten" } diff --git a/homeassistant/components/mailgun/.translations/fr.json b/homeassistant/components/mailgun/.translations/fr.json index 905715de727117..5d86a36b9476cb 100644 --- a/homeassistant/components/mailgun/.translations/fr.json +++ b/homeassistant/components/mailgun/.translations/fr.json @@ -9,8 +9,10 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer Mailgun?" + "description": "\u00cates-vous s\u00fbr de vouloir configurer Mailgun?", + "title": "Configurer le Webhook Mailgun" } - } + }, + "title": "Mailgun" } } \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/es.json b/homeassistant/components/mobile_app/.translations/es.json new file mode 100644 index 00000000000000..e88012b8613a63 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/es.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Abre la aplicaci\u00f3n en el m\u00f3vil para configurar la integraci\u00f3n con Home Assistant. Echa un vistazo a [la documentaci\u00f3n]({apps_url}) para ver una lista de apps compatibles." + }, + "step": { + "confirm": { + "description": "\u00bfQuieres configurar el componente de la aplicaci\u00f3n para el m\u00f3vil?", + "title": "Aplicaci\u00f3n para el m\u00f3vil" + } + }, + "title": "Aplicaci\u00f3n para el m\u00f3vil" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/fr.json b/homeassistant/components/mobile_app/.translations/fr.json new file mode 100644 index 00000000000000..54c945a7a4beea --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/fr.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "install_app": "Ouvrez l'application mobile pour configurer l'int\u00e9gration avec Home Assistant. Voir [la documentation] ( {apps_url} ) pour obtenir une liste des applications compatibles." + }, + "step": { + "confirm": { + "description": "Voulez-vous configurer le composant Application mobile?", + "title": "Application mobile" + } + }, + "title": "Application mobile" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/it.json b/homeassistant/components/mobile_app/.translations/it.json new file mode 100644 index 00000000000000..8c083fad17ef07 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/it.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "App per dispositivi mobili" + } + }, + "title": "App per dispositivi mobili" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/pt.json b/homeassistant/components/mobile_app/.translations/pt.json new file mode 100644 index 00000000000000..1c61180726ca45 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/pt.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "Aplica\u00e7\u00e3o m\u00f3vel" + } + }, + "title": "Aplica\u00e7\u00e3o m\u00f3vel" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/sv.json b/homeassistant/components/mobile_app/.translations/sv.json new file mode 100644 index 00000000000000..bdd94b84a1a9ad --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/sv.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "Vill du st\u00e4lla in mobilappkomponenten?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/zh-Hans.json b/homeassistant/components/mobile_app/.translations/zh-Hans.json new file mode 100644 index 00000000000000..b48ca1e4263bb4 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "install_app": "\u6253\u5f00\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u4ee5\u8bbe\u7f6e\u4e0e Home Assistant \u7684\u96c6\u6210\u3002\u8bf7\u53c2\u9605[\u6587\u6863]({apps_url})\u4ee5\u83b7\u53d6\u517c\u5bb9\u5e94\u7528\u7684\u5217\u8868\u3002" + }, + "step": { + "confirm": { + "description": "\u60a8\u60f3\u8981\u914d\u7f6e\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u7ec4\u4ef6\u5417\uff1f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.es.json b/homeassistant/components/moon/.translations/sensor.es.json index 3ce14cd4c770ea..b3456735754dab 100644 --- a/homeassistant/components/moon/.translations/sensor.es.json +++ b/homeassistant/components/moon/.translations/sensor.es.json @@ -1,10 +1,12 @@ { "state": { - "first_quarter": "Primer cuarto", + "first_quarter": "Cuarto creciente", "full_moon": "Luna llena", - "last_quarter": "\u00daltimo cuarto", + "last_quarter": "Cuarto menguante", "new_moon": "Luna nueva", - "waning_crescent": "Luna menguante", - "waxing_crescent": "Luna creciente" + "waning_crescent": "Menguante", + "waning_gibbous": "Gibosa menguante", + "waxing_crescent": "Nueva visible", + "waxing_gibbous": "Gibosa creciente" } } \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.pt.json b/homeassistant/components/moon/.translations/sensor.pt.json index 14961ab98f0874..c73ff5b2977d66 100644 --- a/homeassistant/components/moon/.translations/sensor.pt.json +++ b/homeassistant/components/moon/.translations/sensor.pt.json @@ -2,11 +2,6 @@ "state": { "first_quarter": "Quarto crescente", "full_moon": "Lua cheia", - "last_quarter": "Quarto minguante", - "new_moon": "Lua nova", - "waning_crescent": "Lua crescente", - "waning_gibbous": "Minguante convexa", - "waxing_crescent": "Lua minguante", - "waxing_gibbous": "Crescente convexa" + "new_moon": "Lua nova" } } \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.th.json b/homeassistant/components/moon/.translations/sensor.th.json new file mode 100644 index 00000000000000..5d65c23226d22c --- /dev/null +++ b/homeassistant/components/moon/.translations/sensor.th.json @@ -0,0 +1,5 @@ +{ + "state": { + "full_moon": "\u0e1e\u0e23\u0e30\u0e08\u0e31\u0e19\u0e17\u0e23\u0e4c\u0e40\u0e15\u0e47\u0e21\u0e14\u0e27\u0e07" + } +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/.translations/ko.json b/homeassistant/components/mqtt/.translations/ko.json index ed552c0d9941bc..e2a1ef6456e0e5 100644 --- a/homeassistant/components/mqtt/.translations/ko.json +++ b/homeassistant/components/mqtt/.translations/ko.json @@ -22,7 +22,7 @@ "data": { "discovery": "\uae30\uae30 \uac80\uc0c9 \ud65c\uc131\ud654" }, - "description": "Hass.io \uc560\ub4dc\uc628 {addon} \uc5d0\uc11c \uc81c\uacf5\ud558\ub294 MQTT \ube0c\ub85c\ucee4\uc5d0 \uc5f0\uacb0\ud558\ub3c4\ub85d Home Assistant \ub97c \uad6c\uc131 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", + "description": "Hass.io \uc560\ub4dc\uc628 {addon} \ub85c(\uc73c\ub85c) MQTT \ube0c\ub85c\ucee4\uc5d0 \uc5f0\uacb0\ud558\ub3c4\ub85d Home Assistant \ub97c \uad6c\uc131 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "title": "Hass.io \uc560\ub4dc\uc628\uc758 MQTT \ube0c\ub85c\ucee4" } }, diff --git a/homeassistant/components/mqtt/.translations/ru.json b/homeassistant/components/mqtt/.translations/ru.json index ad3a90383b12df..aacac084b198d6 100644 --- a/homeassistant/components/mqtt/.translations/ru.json +++ b/homeassistant/components/mqtt/.translations/ru.json @@ -22,8 +22,8 @@ "data": { "discovery": "\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432" }, - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u0434\u043e\u043d Hass.io {addon}?", - "title": "\u0411\u0440\u043e\u043a\u0435\u0440 MQTT \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u0434\u043e\u043d Hass.io" + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443 MQTT (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Hass.io \"{addon}\")?", + "title": "\u0411\u0440\u043e\u043a\u0435\u0440 MQTT (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Hass.io)" } }, "title": "MQTT" diff --git a/homeassistant/components/mqtt/.translations/th.json b/homeassistant/components/mqtt/.translations/th.json index 7ea8785af32e55..293b7e34314ccf 100644 --- a/homeassistant/components/mqtt/.translations/th.json +++ b/homeassistant/components/mqtt/.translations/th.json @@ -3,7 +3,9 @@ "step": { "broker": { "data": { - "discovery": "\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e01\u0e32\u0e23\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e2d\u0e38\u0e1b\u0e01\u0e23\u0e13\u0e4c" + "discovery": "\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e01\u0e32\u0e23\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e2d\u0e38\u0e1b\u0e01\u0e23\u0e13\u0e4c", + "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19", + "username": "\u0e0a\u0e37\u0e48\u0e2d\u0e1c\u0e39\u0e49\u0e43\u0e0a\u0e49" } }, "hassio_confirm": { diff --git a/homeassistant/components/nest/.translations/ca.json b/homeassistant/components/nest/.translations/ca.json index 179c8f20951f4a..b242208791b6ae 100644 --- a/homeassistant/components/nest/.translations/ca.json +++ b/homeassistant/components/nest/.translations/ca.json @@ -17,7 +17,7 @@ "data": { "flow_impl": "Prove\u00efdor" }, - "description": "Tria a amb quin prove\u00efdor d'autenticaci\u00f3 vols autenticar-te amb Nest.", + "description": "Tria quin prove\u00efdor d'autenticaci\u00f3 vols utilitzar per autenticar-te amb Nest.", "title": "Prove\u00efdor d'autenticaci\u00f3" }, "link": { diff --git a/homeassistant/components/nest/.translations/ru.json b/homeassistant/components/nest/.translations/ru.json index ff86c34ac719aa..1c24acd96e427a 100644 --- a/homeassistant/components/nest/.translations/ru.json +++ b/homeassistant/components/nest/.translations/ru.json @@ -4,7 +4,7 @@ "already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", "authorize_url_fail": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", - "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Nest \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/nest/)." + "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Nest \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/nest/)." }, "error": { "internal_error": "\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u0434\u0430", @@ -17,7 +17,7 @@ "data": { "flow_impl": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0432\u0445\u043e\u0434 \u0432 Nest.", + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0432\u0445\u043e\u0434.", "title": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" }, "link": { diff --git a/homeassistant/components/nest/.translations/th.json b/homeassistant/components/nest/.translations/th.json new file mode 100644 index 00000000000000..82ec7f168faffb --- /dev/null +++ b/homeassistant/components/nest/.translations/th.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "invalid_code": "\u0e23\u0e2b\u0e31\u0e2a\u0e44\u0e21\u0e48\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07" + }, + "step": { + "link": { + "data": { + "code": "Pin code" + } + } + }, + "title": "Nest" + } +} \ No newline at end of file diff --git a/homeassistant/components/openuv/.translations/ko.json b/homeassistant/components/openuv/.translations/ko.json index 5e06be81d31b9d..c16481993efcbd 100644 --- a/homeassistant/components/openuv/.translations/ko.json +++ b/homeassistant/components/openuv/.translations/ko.json @@ -12,7 +12,7 @@ "latitude": "\uc704\ub3c4", "longitude": "\uacbd\ub3c4" }, - "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694" + "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc785\ub825" } }, "title": "OpenUV" diff --git a/homeassistant/components/openuv/.translations/ru.json b/homeassistant/components/openuv/.translations/ru.json index 38e261ab6bd865..9683c5d7c3679b 100644 --- a/homeassistant/components/openuv/.translations/ru.json +++ b/homeassistant/components/openuv/.translations/ru.json @@ -7,7 +7,7 @@ "step": { "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API OpenUV", + "api_key": "\u041a\u043b\u044e\u0447 API", "elevation": "\u0412\u044b\u0441\u043e\u0442\u0430", "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430" diff --git a/homeassistant/components/owntracks/.translations/fr.json b/homeassistant/components/owntracks/.translations/fr.json index 46a0f2f2921261..5975c34e78d0a8 100644 --- a/homeassistant/components/owntracks/.translations/fr.json +++ b/homeassistant/components/owntracks/.translations/fr.json @@ -3,6 +3,9 @@ "abort": { "one_instance_allowed": "Une seule instance est n\u00e9cessaire." }, + "create_entry": { + "default": "\n\n Sous Android, ouvrez [l'application OwnTracks] ( {android_url} ), acc\u00e9dez \u00e0 Pr\u00e9f\u00e9rences - > Connexion. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP priv\u00e9 \n - H\u00f4te: {webhook_url} \n - Identification: \n - Nom d'utilisateur: ` ` \n - ID de p\u00e9riph\u00e9rique: ` ` \n\n Sur iOS, ouvrez [l'application OwnTracks] ( {ios_url} ), appuyez sur l'ic\u00f4ne (i) en haut \u00e0 gauche - > param\u00e8tres. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP \n - URL: {webhook_url} \n - Activer l'authentification \n - ID utilisateur: ` ` \n\n {secret} \n \n Voir [la documentation] ( {docs_url} ) pour plus d'informations." + }, "step": { "user": { "description": "\u00cates-vous s\u00fbr de vouloir configurer OwnTracks?", diff --git a/homeassistant/components/point/.translations/ca.json b/homeassistant/components/point/.translations/ca.json index b50a1169a53a18..789068a6339df8 100644 --- a/homeassistant/components/point/.translations/ca.json +++ b/homeassistant/components/point/.translations/ca.json @@ -4,14 +4,14 @@ "already_setup": "Nom\u00e9s pots configurar un compte de Point.", "authorize_url_fail": "S'ha produ\u00eft un error desconegut al generar l'URL d'autoritzaci\u00f3.", "authorize_url_timeout": "S'ha acabat el temps d'espera mentre \u00e9s generava l'URL d'autoritzaci\u00f3.", - "external_setup": "Point s'ha configurat correctament des d'un altre lloc.", + "external_setup": "Point s'ha configurat correctament des d'un altre flux de dades.", "no_flows": "Necessites configurar Point abans de poder autenticar-t'hi. Llegeix les [instruccions](https://www.home-assistant.io/components/point/)." }, "create_entry": { "default": "Autenticaci\u00f3 exitosa amb Minut per als teus dispositiu/s Point." }, "error": { - "follow_link": "Si us plau v\u00e9s a l'enlla\u00e7 i autentica't abans de pr\u00e9mer Enviar", + "follow_link": "V\u00e9s a l'enlla\u00e7 i autentica't abans de pr\u00e9mer Enviar", "no_token": "No s'ha autenticat amb Minut" }, "step": { @@ -23,7 +23,7 @@ "data": { "flow_impl": "Prove\u00efdor" }, - "description": "Tria a trav\u00e9s de quin prove\u00efdor d'autenticaci\u00f3 vols autenticar-te amb Point.", + "description": "Tria quin prove\u00efdor d'autenticaci\u00f3 vols utilitzar per autenticar-te amb Point.", "title": "Prove\u00efdor d'autenticaci\u00f3" } }, diff --git a/homeassistant/components/point/.translations/fr.json b/homeassistant/components/point/.translations/fr.json index ba1b1e27668235..c20b62ef3b6a02 100644 --- a/homeassistant/components/point/.translations/fr.json +++ b/homeassistant/components/point/.translations/fr.json @@ -3,9 +3,22 @@ "abort": { "already_setup": "Vous ne pouvez configurer qu'un compte Point.", "authorize_url_fail": "Erreur inconnue lors de la g\u00e9n\u00e9ration d'une URL d'autorisation.", - "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification d\u00e9pass\u00e9." + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification d\u00e9pass\u00e9.", + "external_setup": "Point correctement configur\u00e9 \u00e0 partir d\u2019un autre flux.", + "no_flows": "Vous devez configurer Point avant de pouvoir vous authentifier avec celui-ci. [Veuillez lire les instructions] (https://www.home-assistant.io/components/point/)." + }, + "create_entry": { + "default": "Authentification r\u00e9ussie avec Minut pour votre (vos) p\u00e9riph\u00e9rique (s) Point" + }, + "error": { + "follow_link": "Veuillez suivre le lien et vous authentifier avant d'appuyer sur Soumettre.", + "no_token": "Non authentifi\u00e9 avec Minut" }, "step": { + "auth": { + "description": "Suivez le lien ci-dessous et acceptez l'acc\u00e8s \u00e0 votre compte Minut, puis revenez et appuyez sur Envoyer ci-dessous. \n\n [Lien] ( {authorization_url} )", + "title": "Point d'authentification" + }, "user": { "data": { "flow_impl": "Fournisseur" diff --git a/homeassistant/components/point/.translations/ko.json b/homeassistant/components/point/.translations/ko.json index 0480b6d7195ee0..8ffcbab1ecc89e 100644 --- a/homeassistant/components/point/.translations/ko.json +++ b/homeassistant/components/point/.translations/ko.json @@ -8,7 +8,7 @@ "no_flows": "Point \ub97c \uc778\uc99d\ud558\uae30 \uc804\uc5d0 Point \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/point/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." }, "create_entry": { - "default": "Point \uc7a5\uce58\ub294 Minut \ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "default": "Point \uae30\uae30\ub294 Minut \ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { "follow_link": "Submit \ubc84\ud2bc\uc744 \ub204\ub974\uae30 \uc804\uc5d0 \ub9c1\ud06c\ub97c \ub530\ub77c \uc778\uc99d\uc744 \ubc1b\uc544\uc8fc\uc138\uc694", diff --git a/homeassistant/components/point/.translations/ru.json b/homeassistant/components/point/.translations/ru.json index d2f3f90cb7792a..3e2bbc4df65a38 100644 --- a/homeassistant/components/point/.translations/ru.json +++ b/homeassistant/components/point/.translations/ru.json @@ -5,25 +5,25 @@ "authorize_url_fail": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "external_setup": "Point \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430.", - "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Point \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/point/)." + "no_flows": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Point \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. [\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/point/)." }, "create_entry": { - "default": "\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { - "follow_link": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u044c \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c", - "no_token": "\u041d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0432\u0445\u043e\u0434 \u0432 Minut" + "follow_link": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u044c \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\".", + "no_token": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0435 \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430." }, "step": { "auth": { "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e [\u0441\u0441\u044b\u043b\u043a\u0435]({authorization_url}) \u0438 \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Minut, \u0437\u0430\u0442\u0435\u043c \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430 \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c.", - "title": "\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0432\u0445\u043e\u0434 \u0432 Point" + "title": "Minut Point" }, "user": { "data": { "flow_impl": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0432\u0445\u043e\u0434 \u0432 Point.", + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0432\u0445\u043e\u0434.", "title": "\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" } }, diff --git a/homeassistant/components/ps4/.translations/es.json b/homeassistant/components/ps4/.translations/es.json index 65798ba4d0c4c6..a159cd0552a779 100644 --- a/homeassistant/components/ps4/.translations/es.json +++ b/homeassistant/components/ps4/.translations/es.json @@ -9,10 +9,12 @@ }, "error": { "login_failed": "No se ha podido emparejar con PlayStation 4. Verifique que el PIN sea correcto.", + "no_ipaddress": "Introduce la direcci\u00f3n IP de la PlayStation 4 que quieres configurar.", "not_ready": "PlayStation 4 no est\u00e1 encendido o conectado a la red." }, "step": { "creds": { + "description": "Credenciales necesarias. Pulsa 'Enviar' y, a continuaci\u00f3n, en la app de segunda pantalla de PS4, actualiza la lista de dispositivos y selecciona el dispositivo 'Home-Assistant' para continuar.", "title": "PlayStation 4" }, "link": { @@ -22,6 +24,15 @@ "name": "Nombre", "region": "Regi\u00f3n" }, + "description": "Introduce la informaci\u00f3n de tu PlayStation 4. Para el 'PIN', ve a los 'Ajustes' en tu PlayStation 4. Despu\u00e9s dir\u00edgete hasta 'Ajustes de conexi\u00f3n de la aplicaci\u00f3n para m\u00f3viles' y selecciona 'A\u00f1adir dispositivo'. Introduce el PIN mostrado. Consulta la [documentaci\u00f3n](https://www.home-assistant.io/components/ps4/) para m\u00e1s informaci\u00f3n.", + "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "Direcci\u00f3n IP (d\u00e9jalo en blanco si usas la detecci\u00f3n autom\u00e1tica).", + "mode": "Modo configuraci\u00f3n" + }, + "description": "Selecciona el modo de configuraci\u00f3n. El campo de direcci\u00f3n IP puede dejarse en blanco si se selecciona la detecci\u00f3n autom\u00e1tica, ya que los dispositivos se detectar\u00e1n autom\u00e1ticamente.", "title": "PlayStation 4" } }, diff --git a/homeassistant/components/ps4/.translations/fr.json b/homeassistant/components/ps4/.translations/fr.json index bb654eed22888e..cfd65c910d9b67 100644 --- a/homeassistant/components/ps4/.translations/fr.json +++ b/homeassistant/components/ps4/.translations/fr.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "\u00c9chec de l'association \u00e0 la PlayStation 4. V\u00e9rifiez que le code PIN est correct.", + "no_ipaddress": "Entrez l'adresse IP de la PlayStation 4 que vous souhaitez configurer.", "not_ready": "PlayStation 4 n'est pas allum\u00e9e ou connect\u00e9e au r\u00e9seau." }, "step": { @@ -25,6 +26,14 @@ }, "description": "Entrez vos informations PlayStation 4. Pour \"Code PIN\", acc\u00e9dez \u00e0 \"Param\u00e8tres\" sur votre console PlayStation 4. Ensuite, acc\u00e9dez \u00e0 \"Param\u00e8tres de connexion de l'application mobile\" et s\u00e9lectionnez \"Ajouter un p\u00e9riph\u00e9rique\". Entrez le code PIN qui est affich\u00e9.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "ip_address": "Adresse IP (laissez vide si vous utilisez la d\u00e9couverte automatique).", + "mode": "Mode de configuration" + }, + "description": "S\u00e9lectionnez le mode de configuration. Le champ Adresse IP peut rester vide si vous s\u00e9lectionnez D\u00e9couverte automatique, car les p\u00e9riph\u00e9riques seront automatiquement d\u00e9couverts.", + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/it.json b/homeassistant/components/ps4/.translations/it.json index 5e83d7bd39c22d..635fbd7b479cca 100644 --- a/homeassistant/components/ps4/.translations/it.json +++ b/homeassistant/components/ps4/.translations/it.json @@ -9,6 +9,7 @@ }, "error": { "login_failed": "Accoppiamento alla PlayStation 4 fallito. Verifica che il PIN sia corretto.", + "no_ipaddress": "Inserisci l'indirizzo IP della PlayStation 4 che desideri configurare.", "not_ready": "La PlayStation 4 non \u00e8 accesa o non \u00e8 collegata alla rete." }, "step": { @@ -25,6 +26,12 @@ }, "description": "Inserisci le informazioni della tua PlayStation 4. Per il \"PIN\", vai su \"Impostazioni\" sulla tua console PlayStation 4. Quindi accedi a \"Impostazioni connessione app mobile\" e seleziona \"Aggiungi dispositivo\". Inserisci il PIN che viene visualizzato.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "mode": "Modalit\u00e0 di configurazione" + }, + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/ko.json b/homeassistant/components/ps4/.translations/ko.json index ba864f07320012..d42586505d950e 100644 --- a/homeassistant/components/ps4/.translations/ko.json +++ b/homeassistant/components/ps4/.translations/ko.json @@ -32,7 +32,7 @@ "ip_address": "IP \uc8fc\uc18c (\uc790\ub3d9 \uac80\uc0c9\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ube44\uc6cc\ub450\uc138\uc694)", "mode": "\uad6c\uc131 \ubaa8\ub4dc" }, - "description": "\uad6c\uc131 \ubaa8\ub4dc\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \uc790\ub3d9 \uac80\uc0c9\uc744 \uc120\ud0dd\ud558\uba74 \uae30\uae30\uac00 \uc790\ub3d9\uc73c\ub85c \uac80\uc0c9\ub418\ubbc0\ub85c IP \uc8fc\uc18c \ud544\ub4dc\ub294 \ube44\uc6cc\ub458 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "description": "\uad6c\uc131 \ubaa8\ub4dc\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \uc790\ub3d9 \uac80\uc0c9\uc744 \uc120\ud0dd\ud558\uba74 \uae30\uae30\uac00 \uc790\ub3d9\uc73c\ub85c \uac80\uc0c9\ub418\ubbc0\ub85c IP \uc8fc\uc18c \ud544\ub4dc\ub294 \ube44\uc6cc\ub450\uc154\ub3c4 \ub429\ub2c8\ub2e4.", "title": "PlayStation 4" } }, diff --git a/homeassistant/components/ps4/.translations/pt.json b/homeassistant/components/ps4/.translations/pt.json index 34a5ebfc4db14d..5d4c8e1228308a 100644 --- a/homeassistant/components/ps4/.translations/pt.json +++ b/homeassistant/components/ps4/.translations/pt.json @@ -7,10 +7,14 @@ "link": { "data": { "code": "PIN", + "ip_address": "Endere\u00e7o de IP", "name": "Nome", "region": "Regi\u00e3o" }, "title": "PlayStation 4" + }, + "mode": { + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/ru.json b/homeassistant/components/ps4/.translations/ru.json index a784a607ac37b8..d69213d7d759a7 100644 --- a/homeassistant/components/ps4/.translations/ru.json +++ b/homeassistant/components/ps4/.translations/ru.json @@ -3,7 +3,7 @@ "abort": { "credential_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.", "devices_configured": "\u0412\u0441\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b.", - "no_devices_found": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 PlayStation 4.", + "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 PlayStation 4 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "port_987_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 987. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/).", "port_997_bind_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043f\u043e\u0440\u0442\u0443 997. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/)." }, diff --git a/homeassistant/components/ps4/.translations/sv.json b/homeassistant/components/ps4/.translations/sv.json index d35efbd4b0071e..642497b107450a 100644 --- a/homeassistant/components/ps4/.translations/sv.json +++ b/homeassistant/components/ps4/.translations/sv.json @@ -25,6 +25,12 @@ }, "description": "Ange din PlayStation 4 information. F\u00f6r 'PIN', navigera till 'Inst\u00e4llningar' p\u00e5 din PlayStation 4 konsol. Navigera sedan till \"Inst\u00e4llningar f\u00f6r mobilappanslutning\" och v\u00e4lj \"L\u00e4gg till enhet\". Ange PIN-koden som visas.", "title": "PlayStation 4" + }, + "mode": { + "data": { + "mode": "Konfigureringsl\u00e4ge" + }, + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/ps4/.translations/th.json b/homeassistant/components/ps4/.translations/th.json index a48089bfdd6ace..b33002bcda85af 100644 --- a/homeassistant/components/ps4/.translations/th.json +++ b/homeassistant/components/ps4/.translations/th.json @@ -12,6 +12,9 @@ "region": "\u0e20\u0e39\u0e21\u0e34\u0e20\u0e32\u0e04" }, "title": "PlayStation 4" + }, + "mode": { + "title": "PlayStation 4" } }, "title": "PlayStation 4" diff --git a/homeassistant/components/rainmachine/.translations/ko.json b/homeassistant/components/rainmachine/.translations/ko.json index 5ce254c4026ae1..4e2df2ca21717f 100644 --- a/homeassistant/components/rainmachine/.translations/ko.json +++ b/homeassistant/components/rainmachine/.translations/ko.json @@ -11,7 +11,7 @@ "password": "\ube44\ubc00\ubc88\ud638", "port": "\ud3ec\ud2b8" }, - "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694" + "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc785\ub825" } }, "title": "RainMachine" diff --git a/homeassistant/components/season/.translations/sensor.zh-Hans.json b/homeassistant/components/season/.translations/sensor.zh-Hans.json index 78801f4b1df70d..e441b1aa8ac62e 100644 --- a/homeassistant/components/season/.translations/sensor.zh-Hans.json +++ b/homeassistant/components/season/.translations/sensor.zh-Hans.json @@ -1,8 +1,8 @@ { "state": { - "autumn": "\u79cb\u5b63", - "spring": "\u6625\u5b63", - "summer": "\u590f\u5b63", - "winter": "\u51ac\u5b63" + "autumn": "\u79cb", + "spring": "\u6625", + "summer": "\u590f", + "winter": "\u51ac" } } \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.th.json b/homeassistant/components/sensor/.translations/moon.th.json new file mode 100644 index 00000000000000..5d65c23226d22c --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.th.json @@ -0,0 +1,5 @@ +{ + "state": { + "full_moon": "\u0e1e\u0e23\u0e30\u0e08\u0e31\u0e19\u0e17\u0e23\u0e4c\u0e40\u0e15\u0e47\u0e21\u0e14\u0e27\u0e07" + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/.translations/ko.json b/homeassistant/components/simplisafe/.translations/ko.json index 8fb056e3f93977..5cbe233a05e64b 100644 --- a/homeassistant/components/simplisafe/.translations/ko.json +++ b/homeassistant/components/simplisafe/.translations/ko.json @@ -11,7 +11,7 @@ "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc774\uba54\uc77c \uc8fc\uc18c" }, - "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694" + "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc785\ub825" } }, "title": "SimpliSafe" diff --git a/homeassistant/components/smartthings/.translations/es.json b/homeassistant/components/smartthings/.translations/es.json index 4edeb153921715..5534b4e3bb3b9e 100644 --- a/homeassistant/components/smartthings/.translations/es.json +++ b/homeassistant/components/smartthings/.translations/es.json @@ -3,18 +3,23 @@ "error": { "app_not_installed": "Por favor aseg\u00farese de haber instalado y autorizado Home Assistant SmartApp y vuelva a intentarlo.", "app_setup_error": "No se pudo configurar el SmartApp. Por favor, int\u00e9ntelo de nuevo.", + "base_url_not_https": "La 'base_url' del componente 'http' debe empezar por 'https://'.", "token_already_setup": "El token ya ha sido configurado.", + "token_forbidden": "El token no tiene los alcances necesarios de OAuth.", "token_invalid_format": "El token debe estar en formato UID/GUID", - "token_unauthorized": "El token no es v\u00e1lido o ya no est\u00e1 autorizado." + "token_unauthorized": "El token no es v\u00e1lido o ya no est\u00e1 autorizado.", + "webhook_error": "SmartThings no ha podido validar el endpoint configurado en 'base_url'. Por favor, revisa los requisitos del componente." }, "step": { "user": { "data": { "access_token": "Token de acceso" }, - "title": "Ingresar token de acceso personal" + "description": "Por favor, introduce el [token de acceso personal]({token_url}) de SmartThings que se haya creado seg\u00fan las [instrucciones]({component_url}).", + "title": "Introduce el token de acceso personal" }, "wait_install": { + "description": "Por favor, instala Home Assistant SmartApp en al menos una ubicaci\u00f3n y pulsa en enviar.", "title": "Instalar SmartApp" } }, diff --git a/homeassistant/components/smartthings/.translations/ru.json b/homeassistant/components/smartthings/.translations/ru.json index 6e34cf8a49acfd..575c593d5a481e 100644 --- a/homeassistant/components/smartthings/.translations/ru.json +++ b/homeassistant/components/smartthings/.translations/ru.json @@ -16,7 +16,7 @@ "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430" }, "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 [\u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430]({token_url}) SmartThings, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438]({component_url}).", - "title": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430" + "title": "SmartThings" }, "wait_install": { "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 SmartApp 'Home Assistant' \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c**.", diff --git a/homeassistant/components/sonos/.translations/ko.json b/homeassistant/components/sonos/.translations/ko.json index 0b2e2a1875ce72..4ca3d621599d9e 100644 --- a/homeassistant/components/sonos/.translations/ko.json +++ b/homeassistant/components/sonos/.translations/ko.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Sonos \uc7a5\uce58\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_devices_found": "Sonos \uae30\uae30\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\ud558\ub098\uc758 Sonos \ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { diff --git a/homeassistant/components/tellduslive/.translations/ko.json b/homeassistant/components/tellduslive/.translations/ko.json index 8a68e303aff22b..6b04e867861ed1 100644 --- a/homeassistant/components/tellduslive/.translations/ko.json +++ b/homeassistant/components/tellduslive/.translations/ko.json @@ -13,14 +13,14 @@ "step": { "auth": { "description": "TelldusLive \uacc4\uc815\uc744 \uc5f0\uacb0\ud558\ub824\uba74:\n 1. \ud558\ub2e8\uc758 \ub9c1\ud06c\ub97c \ud074\ub9ad\ud574\uc8fc\uc138\uc694\n 2. Telldus Live \uc5d0 \ub85c\uadf8\uc778 \ud558\uc138\uc694\n 3. Authorize **{app_name}** (**Yes** \ub97c \ud074\ub9ad\ud558\uc138\uc694).\n 4. \ub2e4\uc2dc \uc5ec\uae30\ub85c \ub3cc\uc544\uc640\uc11c **SUBMIT** \uc744 \ud074\ub9ad\ud558\uc138\uc694.\n\n [TelldusLive \uacc4\uc815 \uc5f0\uacb0\ud558\uae30]({auth_url})", - "title": "TelldusLive \uc5d0 \ub300\ud55c \uc778\uc99d" + "title": "TelldusLive \uc778\uc99d" }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8" }, "description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.", - "title": "\uc5d4\ub4dc \ud3ec\uc778\ud2b8\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694." + "title": "\uc5d4\ub4dc\ud3ec\uc778\ud2b8 \uc120\ud0dd" } }, "title": "Telldus Live" diff --git a/homeassistant/components/toon/.translations/es.json b/homeassistant/components/toon/.translations/es.json new file mode 100644 index 00000000000000..db5745ca090a11 --- /dev/null +++ b/homeassistant/components/toon/.translations/es.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "client_id": "El ID de cliente en la configuraci\u00f3n no es v\u00e1lido.", + "client_secret": "El secreto de la configuraci\u00f3n no es v\u00e1lido.", + "no_agreements": "Esta cuenta no tiene pantallas Toon.", + "no_app": "Es necesario configurar Toon antes de poder autenticarse con \u00e9l. [Por favor, lee las instrucciones](https://www.home-assistant.io/components/toon/).", + "unknown_auth_fail": "Se ha producido un error inesperado al autenticar." + }, + "error": { + "credentials": "Las credenciales proporcionadas no son v\u00e1lidas.", + "display_exists": "La pantalla seleccionada ya est\u00e1 configurada." + }, + "step": { + "authenticate": { + "data": { + "password": "Contrase\u00f1a", + "tenant": "Inquilino", + "username": "Nombre de usuario" + }, + "description": "Identif\u00edcate con tu cuenta de Eneco Toon (no con la cuenta de desarrollador).", + "title": "Vincular tu cuenta Toon" + }, + "display": { + "data": { + "display": "Elige una pantalla" + }, + "description": "Selecciona la pantalla Toon que quieres conectar.", + "title": "Seleccionar pantalla" + } + }, + "title": "Toon" + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/.translations/pt.json b/homeassistant/components/toon/.translations/pt.json new file mode 100644 index 00000000000000..ebec0df356fd38 --- /dev/null +++ b/homeassistant/components/toon/.translations/pt.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "authenticate": { + "data": { + "password": "Palavra-passe", + "username": "Nome de Utilizador" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/.translations/ru.json b/homeassistant/components/toon/.translations/ru.json index 0cc162218e99c6..012aa65187c28b 100644 --- a/homeassistant/components/toon/.translations/ru.json +++ b/homeassistant/components/toon/.translations/ru.json @@ -4,7 +4,7 @@ "client_id": "Client ID \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d.", "client_secret": "Client secret \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d.", "no_agreements": "\u0423 \u044d\u0442\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435\u0442 \u0434\u0438\u0441\u043f\u043b\u0435\u0435\u0432 Toon.", - "no_app": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Toon \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/toon/).", + "no_app": "\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Toon \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/toon/).", "unknown_auth_fail": "\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { diff --git a/homeassistant/components/toon/.translations/th.json b/homeassistant/components/toon/.translations/th.json new file mode 100644 index 00000000000000..896d9ba8176c45 --- /dev/null +++ b/homeassistant/components/toon/.translations/th.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "authenticate": { + "data": { + "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19", + "username": "\u0e0a\u0e37\u0e48\u0e2d\u0e1c\u0e39\u0e49\u0e43\u0e0a\u0e49" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tplink/.translations/ko.json b/homeassistant/components/tplink/.translations/ko.json index c31e686a76d8c3..05bebdd14554d3 100644 --- a/homeassistant/components/tplink/.translations/ko.json +++ b/homeassistant/components/tplink/.translations/ko.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "no_devices_found": "TP-Link \uc7a5\uce58\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_devices_found": "TP-Link \uae30\uae30\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\ud558\ub098\uc758 TP-Link \ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { "confirm": { - "description": "TP-Link \uc2a4\ub9c8\ud2b8 \uc7a5\uce58\ub97c \uc124\uc815 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", + "description": "TP-Link \uc2a4\ub9c8\ud2b8 \uae30\uae30\ub97c \uc124\uc815 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "title": "TP-Link Smart Home" } }, diff --git a/homeassistant/components/tplink/.translations/pt.json b/homeassistant/components/tplink/.translations/pt.json new file mode 100644 index 00000000000000..1d9fb41fc8ce11 --- /dev/null +++ b/homeassistant/components/tplink/.translations/pt.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "TP-Link Smart Home" + } + }, + "title": "TP-Link Smart Home" + } +} \ No newline at end of file diff --git a/homeassistant/components/tplink/.translations/th.json b/homeassistant/components/tplink/.translations/th.json new file mode 100644 index 00000000000000..80740c9190f06f --- /dev/null +++ b/homeassistant/components/tplink/.translations/th.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "TP-Link Smart Home" + } +} \ No newline at end of file diff --git a/homeassistant/components/tradfri/.translations/ru.json b/homeassistant/components/tradfri/.translations/ru.json index 352579f810cebd..e1e0c950618ca0 100644 --- a/homeassistant/components/tradfri/.translations/ru.json +++ b/homeassistant/components/tradfri/.translations/ru.json @@ -15,7 +15,7 @@ "security_code": "\u041a\u043e\u0434 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438" }, "description": "\u041a\u043e\u0434 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 \u0437\u0430\u0434\u043d\u0435\u0439 \u043f\u0430\u043d\u0435\u043b\u0438 \u0448\u043b\u044e\u0437\u0430.", - "title": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 " + "title": "IKEA TR\u00c5DFRI" } }, "title": "IKEA TR\u00c5DFRI" diff --git a/homeassistant/components/unifi/.translations/fr.json b/homeassistant/components/unifi/.translations/fr.json index 767962e37eba39..9e567fcc394a75 100644 --- a/homeassistant/components/unifi/.translations/fr.json +++ b/homeassistant/components/unifi/.translations/fr.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Le contr\u00f4leur est d\u00e9j\u00e0 configur\u00e9", "user_privilege": "L'utilisateur doit \u00eatre administrateur" }, "error": { diff --git a/homeassistant/components/unifi/.translations/th.json b/homeassistant/components/unifi/.translations/th.json index 178d052c722d2c..3c828bb1182684 100644 --- a/homeassistant/components/unifi/.translations/th.json +++ b/homeassistant/components/unifi/.translations/th.json @@ -5,7 +5,8 @@ "data": { "host": "\u0e42\u0e2e\u0e2a\u0e15\u0e4c", "password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19" - } + }, + "title": "\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 UniFi Controller" } } } diff --git a/homeassistant/components/upnp/.translations/fr.json b/homeassistant/components/upnp/.translations/fr.json index d1ff04d4824e68..a87ea9ec9c79d0 100644 --- a/homeassistant/components/upnp/.translations/fr.json +++ b/homeassistant/components/upnp/.translations/fr.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "UPnP / IGD est d\u00e9j\u00e0 configur\u00e9", + "incomplete_device": "Ignorer un p\u00e9riph\u00e9rique UPnP incomplet", "no_devices_discovered": "Aucun UPnP / IGD d\u00e9couvert", "no_devices_found": "Aucun p\u00e9riph\u00e9rique UPnP / IGD trouv\u00e9 sur le r\u00e9seau.", "no_sensors_or_port_mapping": "Activer au moins les capteurs ou la cartographie des ports", diff --git a/homeassistant/components/zha/.translations/fr.json b/homeassistant/components/zha/.translations/fr.json index de1a2274dd3489..48328aed878189 100644 --- a/homeassistant/components/zha/.translations/fr.json +++ b/homeassistant/components/zha/.translations/fr.json @@ -9,6 +9,7 @@ "step": { "user": { "data": { + "radio_type": "Type de radio", "usb_path": "Chemin du p\u00e9riph\u00e9rique USB" }, "title": "ZHA" diff --git a/homeassistant/components/zha/.translations/ko.json b/homeassistant/components/zha/.translations/ko.json index ffeaf4588e6808..dfe4167cfcc5b9 100644 --- a/homeassistant/components/zha/.translations/ko.json +++ b/homeassistant/components/zha/.translations/ko.json @@ -4,7 +4,7 @@ "single_instance_allowed": "\ud558\ub098\uc758 ZHA \ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "error": { - "cannot_connect": "ZHA \uc7a5\uce58\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + "cannot_connect": "ZHA \uae30\uae30\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." }, "step": { "user": { diff --git a/homeassistant/components/zone/.translations/th.json b/homeassistant/components/zone/.translations/th.json new file mode 100644 index 00000000000000..e39765f2da2931 --- /dev/null +++ b/homeassistant/components/zone/.translations/th.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "name_exists": "\u0e21\u0e35\u0e0a\u0e37\u0e48\u0e2d\u0e19\u0e35\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e41\u0e25\u0e49\u0e27" + }, + "step": { + "init": { + "data": { + "latitude": "\u0e40\u0e2a\u0e49\u0e19\u0e23\u0e38\u0e49\u0e07", + "longitude": "\u0e40\u0e2a\u0e49\u0e19\u0e41\u0e27\u0e07", + "name": "\u0e0a\u0e37\u0e48\u0e2d" + } + } + }, + "title": "\u0e42\u0e0b\u0e19" + } +} \ No newline at end of file From 4110bd0acffdf13808d3ec54bb54577301dca225 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Tue, 9 Apr 2019 11:21:00 -0500 Subject: [PATCH 530/605] Add support for when device is not logged in to HEOS (#22913) --- homeassistant/components/heos/__init__.py | 25 ++++++++----- homeassistant/components/heos/media_player.py | 2 +- tests/components/heos/conftest.py | 2 + tests/components/heos/test_init.py | 37 ++++++++++++++++--- tests/components/heos/test_media_player.py | 33 ++++++++++++++++- 5 files changed, 82 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index ffbd8ebffd45f2..084444be4ea87f 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -75,12 +75,16 @@ async def disconnect_controller(event): await controller.disconnect() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) + # Get players and sources try: - players, favorites, inputs = await asyncio.gather( - controller.get_players(), - controller.get_favorites(), - controller.get_input_sources() - ) + players = await controller.get_players() + favorites = {} + if controller.is_signed_in: + favorites = await controller.get_favorites() + else: + _LOGGER.warning("%s is not logged in to your HEOS account and will" + " be unable to retrieve your favorites", host) + inputs = await controller.get_input_sources() except (asyncio.TimeoutError, ConnectionError, CommandError) as error: await controller.disconnect() _LOGGER.debug("Unable to retrieve players and sources: %s", error, @@ -175,9 +179,11 @@ async def get_sources(): retry_attempts = 0 while True: try: - return await asyncio.gather( - controller.get_favorites(), - controller.get_input_sources()) + favorites = {} + if controller.is_signed_in: + favorites = await controller.get_favorites() + inputs = await controller.get_input_sources() + return favorites, inputs except (asyncio.TimeoutError, ConnectionError, CommandError) \ as error: if retry_attempts < self.max_retry_attempts: @@ -192,7 +198,8 @@ async def get_sources(): return async def update_sources(event): - if event in const.EVENT_SOURCES_CHANGED: + if event in (const.EVENT_SOURCES_CHANGED, + const.EVENT_USER_CHANGED): sources = await get_sources() # If throttled, it will return None if sources: diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 466c9ae8faa938..72d42f8f66f6bb 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -141,7 +141,7 @@ async def async_set_shuffle(self, shuffle): async def async_set_volume_level(self, volume): """Set volume level, range 0..1.""" - await self._player.set_volume(volume * 100) + await self._player.set_volume(int(volume * 100)) async def async_update(self): """Update supported features of the player.""" diff --git a/tests/components/heos/conftest.py b/tests/components/heos/conftest.py index db675a24ee095f..211153b1cc7db6 100644 --- a/tests/components/heos/conftest.py +++ b/tests/components/heos/conftest.py @@ -28,6 +28,8 @@ def controller_fixture(players, favorites, input_sources, dispatcher): mock_heos.players = players mock_heos.get_favorites.return_value = favorites mock_heos.get_input_sources.return_value = input_sources + mock_heos.is_signed_in = True + mock_heos.signed_in_username = "user@user.com" yield mock_heos diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py index b6bc3e24e1ac96..408b2f7d088bb0 100644 --- a/tests/components/heos/test_init.py +++ b/tests/components/heos/test_init.py @@ -5,8 +5,7 @@ from pyheos import CommandError, const import pytest -from homeassistant.components.heos import ( - SourceManager, async_setup_entry, async_unload_entry) +from homeassistant.components.heos import async_setup_entry, async_unload_entry from homeassistant.components.heos.const import ( DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN) from homeassistant.components.media_player.const import ( @@ -61,7 +60,7 @@ async def test_async_setup_no_config_returns_true(hass, config_entry): async def test_async_setup_entry_loads_platforms( - hass, config_entry, controller): + hass, config_entry, controller, input_sources, favorites): """Test load connects to heos, retrieves players, and loads platforms.""" config_entry.add_to_hass(hass) with patch.object( @@ -71,10 +70,39 @@ async def test_async_setup_entry_loads_platforms( await hass.async_block_till_done() assert forward_mock.call_count == 1 assert controller.connect.call_count == 1 + assert controller.get_players.call_count == 1 + assert controller.get_favorites.call_count == 1 + assert controller.get_input_sources.call_count == 1 + controller.disconnect.assert_not_called() + assert hass.data[DOMAIN][DATA_CONTROLLER] == controller + assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players + assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == favorites + assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources + + +async def test_async_setup_entry_not_signed_in_loads_platforms( + hass, config_entry, controller, input_sources, caplog): + """Test setup does not retrieve favorites when not logged in.""" + config_entry.add_to_hass(hass) + controller.is_signed_in = False + controller.signed_in_username = None + with patch.object( + hass.config_entries, 'async_forward_entry_setup') as forward_mock: + assert await async_setup_entry(hass, config_entry) + # Assert platforms loaded + await hass.async_block_till_done() + assert forward_mock.call_count == 1 + assert controller.connect.call_count == 1 + assert controller.get_players.call_count == 1 + assert controller.get_favorites.call_count == 0 + assert controller.get_input_sources.call_count == 1 controller.disconnect.assert_not_called() assert hass.data[DOMAIN][DATA_CONTROLLER] == controller assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players - assert isinstance(hass.data[DOMAIN][DATA_SOURCE_MANAGER], SourceManager) + assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == {} + assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources + assert "127.0.0.1 is not logged in to your HEOS account and will be " \ + "unable to retrieve your favorites" in caplog.text async def test_async_setup_entry_connect_failure( @@ -138,4 +166,3 @@ async def test_update_sources_retry(hass, config_entry, config, controller, while "Unable to update sources" not in caplog.text: await asyncio.sleep(0.1) assert controller.get_favorites.call_count == 2 - assert controller.get_input_sources.call_count == 2 diff --git a/tests/components/heos/test_media_player.py b/tests/components/heos/test_media_player.py index 2f8d7dfc1e93ca..dd36c2c013de02 100644 --- a/tests/components/heos/test_media_player.py +++ b/tests/components/heos/test_media_player.py @@ -116,7 +116,12 @@ async def test_updates_start_from_signals( state = hass.states.get('media_player.test_player') assert state.state == STATE_PLAYING - # Test sources event update + +async def test_updates_from_sources_updated( + hass, config_entry, config, controller, input_sources): + """Tests player updates from changes in sources list.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] event = asyncio.Event() async def set_signal(): @@ -124,11 +129,34 @@ async def set_signal(): hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_HEOS_SOURCES_UPDATED, set_signal) - favorites.clear() + input_sources.clear() player.heos.dispatcher.send( const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) await event.wait() source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list + assert len(source_list) == 2 + state = hass.states.get('media_player.test_player') + assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list + + +async def test_updates_from_user_changed( + hass, config_entry, config, controller): + """Tests player updates from changes in user.""" + await setup_platform(hass, config_entry, config) + player = controller.players[1] + event = asyncio.Event() + + async def set_signal(): + event.set() + hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_HEOS_SOURCES_UPDATED, set_signal) + + controller.is_signed_in = False + controller.signed_in_username = None + player.heos.dispatcher.send( + const.SIGNAL_CONTROLLER_EVENT, const.EVENT_USER_CHANGED) + await event.wait() + source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list assert len(source_list) == 1 state = hass.states.get('media_player.test_player') assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list @@ -194,6 +222,7 @@ async def test_services(hass, config_entry, config, controller): {ATTR_ENTITY_ID: 'media_player.test_player', ATTR_MEDIA_VOLUME_LEVEL: 1}, blocking=True) player.set_volume.assert_called_once_with(100) + assert isinstance(player.set_volume.call_args[0][0], int) async def test_select_favorite( From cac00f5b268900357918ab9027442f4a0e32085c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 9 Apr 2019 09:30:32 -0700 Subject: [PATCH 531/605] Test for circular dependencies using manifests (#22908) * Integration dependencies * Lint * Lint * Fix one test * Lint * Fix load custom component integration Fix async issue Add circular dependency detection in manifest validation * Fix test * Address review comment * Apply suggestions from code review Co-Authored-By: balloob --- homeassistant/bootstrap.py | 50 ++--- homeassistant/loader.py | 204 +++++++++++++++--- homeassistant/setup.py | 4 +- script/manifest/validate.py | 30 ++- tests/common.py | 18 +- tests/components/mobile_app/__init__.py | 4 +- tests/test_bootstrap.py | 4 +- tests/test_loader.py | 66 ++++-- tests/test_setup.py | 14 +- .../test_embedded/__init__.py | 1 + .../test_package/manifest.json | 8 + 11 files changed, 314 insertions(+), 89 deletions(-) create mode 100644 tests/testing_config/custom_components/test_package/manifest.json diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 435ec317985315..c27dde9f9408c3 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -128,16 +128,17 @@ async def async_from_config_dict(config: Dict[str, Any], hass.config_entries = config_entries.ConfigEntries(hass, config) await hass.config_entries.async_initialize() - components = _get_components(hass, config) + domains = _get_domains(hass, config) # Resolve all dependencies of all components. - for component in list(components): - try: - components.update(loader.component_dependencies(hass, component)) - except loader.LoaderError: - # Ignore it, or we'll break startup - # It will be properly handled during setup. - pass + for dep_domains in await asyncio.gather(*[ + loader.async_component_dependencies(hass, domain) + for domain in domains + ], return_exceptions=True): + # Result is either a set or an exception. We ignore exceptions + # It will be properly handled during setup of the domain. + if isinstance(dep_domains, set): + domains.update(dep_domains) # setup components res = await core_component.async_setup(hass, config) @@ -151,10 +152,10 @@ async def async_from_config_dict(config: Dict[str, Any], _LOGGER.info("Home Assistant core initialized") # stage 0, load logging components - for component in components: - if component in LOGGING_COMPONENT: + for domain in domains: + if domain in LOGGING_COMPONENT: hass.async_create_task( - async_setup_component(hass, component, config)) + async_setup_component(hass, domain, config)) await hass.async_block_till_done() @@ -165,18 +166,18 @@ async def async_from_config_dict(config: Dict[str, Any], hass.helpers.area_registry.async_get_registry()) # stage 1 - for component in components: - if component in FIRST_INIT_COMPONENT: + for domain in domains: + if domain in FIRST_INIT_COMPONENT: hass.async_create_task( - async_setup_component(hass, component, config)) + async_setup_component(hass, domain, config)) await hass.async_block_till_done() # stage 2 - for component in components: - if component in FIRST_INIT_COMPONENT or component in LOGGING_COMPONENT: + for domain in domains: + if domain in FIRST_INIT_COMPONENT or domain in LOGGING_COMPONENT: continue - hass.async_create_task(async_setup_component(hass, component, config)) + hass.async_create_task(async_setup_component(hass, domain, config)) await hass.async_block_till_done() @@ -398,18 +399,17 @@ async def async_mount_local_lib_path(config_dir: str) -> str: @core.callback -def _get_components(hass: core.HomeAssistant, - config: Dict[str, Any]) -> Set[str]: - """Get components to set up.""" +def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]: + """Get domains of components to set up.""" # Filter out the repeating and common config section [homeassistant] - components = set(key.split(' ')[0] for key in config.keys() - if key != core.DOMAIN) + domains = set(key.split(' ')[0] for key in config.keys() + if key != core.DOMAIN) # Add config entry domains - components.update(hass.config_entries.async_domains()) # type: ignore + domains.update(hass.config_entries.async_domains()) # type: ignore # Make sure the Hass.io component is loaded if 'HASSIO' in os.environ: - components.add('hassio') + domains.add('hassio') - return components + return domains diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 4ca19935206257..a1dbad3439ee4d 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -12,17 +12,28 @@ """ import functools as ft import importlib +import json import logging +import pathlib import sys from types import ModuleType -from typing import Optional, Set, TYPE_CHECKING, Callable, Any, TypeVar, List # noqa pylint: disable=unused-import +from typing import ( + Optional, + Set, + TYPE_CHECKING, + Callable, + Any, + TypeVar, + List, + Dict +) from homeassistant.const import PLATFORM_FORMAT # Typing imports that create a circular dependency # pylint: disable=using-constant-test,unused-import if TYPE_CHECKING: - from homeassistant.core import HomeAssistant # NOQA + from homeassistant.core import HomeAssistant # noqa CALLABLE_T = TypeVar('CALLABLE_T', bound=Callable) # noqa pylint: disable=invalid-name @@ -34,17 +45,146 @@ DATA_KEY = 'components' +DATA_INTEGRATIONS = 'integrations' PACKAGE_CUSTOM_COMPONENTS = 'custom_components' PACKAGE_BUILTIN = 'homeassistant.components' LOOKUP_PATHS = [PACKAGE_CUSTOM_COMPONENTS, PACKAGE_BUILTIN] COMPONENTS_WITH_BAD_PLATFORMS = ['automation', 'mqtt', 'telegram_bot'] +_UNDEF = object() + + +def manifest_from_legacy_module(module: Any) -> Dict: + """Generate a manifest from a legacy module.""" + return { + 'domain': module.DOMAIN, + 'name': module.DOMAIN, + 'documentation': None, + 'requirements': getattr(module, 'REQUIREMENTS', []), + 'dependencies': getattr(module, 'DEPENDENCIES', []), + 'codeowners': [], + } + + +class Integration: + """An integration in Home Assistant.""" + + @classmethod + def resolve_from_root(cls, hass: 'HomeAssistant', root_module: Any, + domain: str) -> 'Optional[Integration]': + """Resolve an integration from a root module.""" + for base in root_module.__path__: + manifest_path = ( + pathlib.Path(base) / domain / 'manifest.json' + ) + + if not manifest_path.is_file(): + continue + + try: + manifest = json.loads(manifest_path.read_text()) + except ValueError as err: + _LOGGER.error("Error parsing manifest.json file at %s: %s", + manifest_path, err) + continue + + return cls( + hass, "{}.{}".format(root_module.__name__, domain), manifest + ) + + return None + + @classmethod + def resolve_legacy(cls, hass: 'HomeAssistant', domain: str) \ + -> 'Optional[Integration]': + """Resolve legacy component. + + Will create a stub manifest. + """ + comp = get_component(hass, domain) + + if comp is None: + return None + + return cls( + hass, comp.__name__, manifest_from_legacy_module(comp) + ) + + def __init__(self, hass: 'HomeAssistant', pkg_path: str, manifest: Dict): + """Initialize an integration.""" + self.hass = hass + self.pkg_path = pkg_path + self.name = manifest['name'] # type: str + self.domain = manifest['domain'] # type: str + self.dependencies = manifest['dependencies'] # type: List[str] + self.requirements = manifest['requirements'] # type: List[str] + + def get_component(self) -> Any: + """Return the component.""" + return importlib.import_module(self.pkg_path) + + def get_platform(self, platform_name: str) -> Any: + """Return a platform for an integration.""" + return importlib.import_module( + "{}.{}".format(self.pkg_path, platform_name) + ) + + +async def async_get_integration(hass: 'HomeAssistant', domain: str)\ + -> Integration: + """Get an integration.""" + cache = hass.data.get(DATA_INTEGRATIONS) + if cache is None: + if not _async_mount_config_dir(hass): + raise IntegrationNotFound(domain) + cache = hass.data[DATA_INTEGRATIONS] = {} + + integration = cache.get(domain, _UNDEF) # type: Optional[Integration] + + if integration is _UNDEF: + pass + elif integration is None: + raise IntegrationNotFound(domain) + else: + return integration + + try: + import custom_components + integration = await hass.async_add_executor_job( + Integration.resolve_from_root, hass, custom_components, domain + ) + if integration is not None: + cache[domain] = integration + return integration + + except ImportError: + pass + + from homeassistant import components + + integration = await hass.async_add_executor_job( + Integration.resolve_from_root, hass, components, domain + ) + + if integration is not None: + cache[domain] = integration + return integration + + integration = await hass.async_add_executor_job( + Integration.resolve_legacy, hass, domain + ) + cache[domain] = integration + + if not integration: + raise IntegrationNotFound(domain) + + return integration class LoaderError(Exception): """Loader base error.""" -class ComponentNotFound(LoaderError): +class IntegrationNotFound(LoaderError): """Raised when a component is not found.""" def __init__(self, domain: str) -> None: @@ -169,12 +309,8 @@ def _load_file(hass, # type: HomeAssistant cache = hass.data.get(DATA_KEY) if cache is None: - if hass.config.config_dir is None: - _LOGGER.error("Can't load components - config dir is not set") + if not _async_mount_config_dir(hass): return None - # Only insert if it's not there (happens during tests) - if sys.path[0] != hass.config.config_dir: - sys.path.insert(0, hass.config.config_dir) cache = hass.data[DATA_KEY] = {} for path in ('{}.{}'.format(base, comp_or_platform) @@ -294,46 +430,58 @@ def bind_hass(func: CALLABLE_T) -> CALLABLE_T: return func -def component_dependencies(hass, # type: HomeAssistant - comp_name: str) -> Set[str]: +async def async_component_dependencies(hass, # type: HomeAssistant + domain: str) -> Set[str]: """Return all dependencies and subdependencies of components. Raises CircularDependency if a circular dependency is found. - - Async friendly. """ - return _component_dependencies(hass, comp_name, set(), set()) + return await _async_component_dependencies(hass, domain, set(), set()) -def _component_dependencies(hass, # type: HomeAssistant - comp_name: str, loaded: Set[str], - loading: Set) -> Set[str]: +async def _async_component_dependencies(hass, # type: HomeAssistant + domain: str, loaded: Set[str], + loading: Set) -> Set[str]: """Recursive function to get component dependencies. Async friendly. """ - component = get_component(hass, comp_name) + integration = await async_get_integration(hass, domain) - if component is None: - raise ComponentNotFound(comp_name) + if integration is None: + raise IntegrationNotFound(domain) - loading.add(comp_name) + loading.add(domain) - for dependency in getattr(component, 'DEPENDENCIES', []): + for dependency_domain in integration.dependencies: # Check not already loaded - if dependency in loaded: + if dependency_domain in loaded: continue # If we are already loading it, we have a circular dependency. - if dependency in loading: - raise CircularDependency(comp_name, dependency) + if dependency_domain in loading: + raise CircularDependency(domain, dependency_domain) - dep_loaded = _component_dependencies( - hass, dependency, loaded, loading) + dep_loaded = await _async_component_dependencies( + hass, dependency_domain, loaded, loading) loaded.update(dep_loaded) - loaded.add(comp_name) - loading.remove(comp_name) + loaded.add(domain) + loading.remove(domain) return loaded + + +def _async_mount_config_dir(hass, # type: HomeAssistant + ) -> bool: + """Mount config dir in order to load custom_component. + + Async friendly but not a coroutine. + """ + if hass.config.config_dir is None: + _LOGGER.error("Can't load components - config dir is not set") + return False + if hass.config.config_dir not in sys.path: + sys.path.insert(0, hass.config.config_dir) + return True diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 29c8e22d45d902..0e294200b5f214 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -108,8 +108,8 @@ def log_error(msg: str, link: bool = True) -> None: # Validate all dependencies exist and there are no circular dependencies try: - loader.component_dependencies(hass, domain) - except loader.ComponentNotFound as err: + await loader.async_component_dependencies(hass, domain) + except loader.IntegrationNotFound as err: _LOGGER.error( "Not setting up %s because we are unable to resolve " "(sub)dependency %s", domain, err.domain) diff --git a/script/manifest/validate.py b/script/manifest/validate.py index d0d59529d208df..e5db1c9368c530 100755 --- a/script/manifest/validate.py +++ b/script/manifest/validate.py @@ -18,10 +18,16 @@ }) -components_path = pathlib.Path('homeassistant/components') +COMPONENTS_PATH = pathlib.Path('homeassistant/components') -def validate_integration(path): +def validate_dependency(path, dependency, loaded, loading): + """Validate dependency is exist and no circular dependency.""" + dep_path = path.parent / dependency + return validate_integration(dep_path, loaded, loading) + + +def validate_integration(path, loaded, loading): """Validate that an integrations has a valid manifest.""" errors = [] path = pathlib.Path(path) @@ -29,7 +35,7 @@ def validate_integration(path): manifest_path = path / 'manifest.json' if not manifest_path.is_file(): - errors.append('File manifest.json not found') + errors.append('Manifest file {} not found'.format(manifest_path)) return errors # Fatal error try: @@ -47,10 +53,18 @@ def validate_integration(path): errors.append('Domain does not match dir name') for dep in manifest['dependencies']: - dep_manifest = path.parent / dep / 'manifest.json' - if not dep_manifest.is_file(): - errors.append("Unable to find dependency {}".format(dep)) + if dep in loaded: + continue + if dep in loading: + errors.append("Found circular dependency {} in {}".format( + dep, path + )) + continue + loading.add(dep) + + errors.extend(validate_dependency(path, dep, loaded, loading)) + loaded.add(path.name) return errors @@ -58,11 +72,11 @@ def validate_all(): """Validate all integrations.""" invalid = [] - for fil in components_path.iterdir(): + for fil in COMPONENTS_PATH.iterdir(): if fil.is_file() or fil.name == '__pycache__': continue - errors = validate_integration(fil) + errors = validate_integration(fil, set(), set()) if errors: invalid.append((fil, errors)) diff --git a/tests/common.py b/tests/common.py index e04c8347c095fc..2f89d91e4d3c40 100644 --- a/tests/common.py +++ b/tests/common.py @@ -16,7 +16,7 @@ import homeassistant.util.dt as date_util import homeassistant.util.yaml as yaml -from homeassistant import auth, config_entries, core as ha +from homeassistant import auth, config_entries, core as ha, loader from homeassistant.auth import ( models as auth_models, auth_store, providers as auth_providers, permissions as auth_permissions) @@ -35,6 +35,7 @@ from homeassistant.util.async_ import ( run_callback_threadsafe, run_coroutine_threadsafe) + _TEST_INSTANCE_PORT = SERVER_PORT _LOGGER = logging.getLogger(__name__) INSTANCES = [] @@ -894,3 +895,18 @@ async def flush_store(store): async def get_system_health_info(hass, domain): """Get system health info.""" return await hass.data['system_health']['info'][domain](hass) + + +def mock_integration(hass, module): + """Mock an integration.""" + integration = loader.Integration( + hass, 'homeassisant.components.{}'.format(module.DOMAIN), + loader.manifest_from_legacy_module(module)) + integration.get_component = lambda: module + + # Backwards compat + loader.set_component(hass, module.DOMAIN, module) + + hass.data.setdefault( + loader.DATA_INTEGRATIONS, {} + )[module.DOMAIN] = integration diff --git a/tests/components/mobile_app/__init__.py b/tests/components/mobile_app/__init__.py index cf617ff05289f7..98c7a20b059d0e 100644 --- a/tests/components/mobile_app/__init__.py +++ b/tests/components/mobile_app/__init__.py @@ -55,7 +55,7 @@ async def webhook_client(hass, aiohttp_client, hass_storage, hass_admin_user): } await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) - + await hass.async_block_till_done() return await aiohttp_client(hass.http.app) @@ -63,6 +63,7 @@ async def webhook_client(hass, aiohttp_client, hass_storage, hass_admin_user): async def authed_api_client(hass, hass_client): """Provide an authenticated client for mobile_app to use.""" await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() return await hass_client() @@ -70,3 +71,4 @@ async def authed_api_client(hass, hass_client): async def setup_ws(hass): """Configure the websocket_api component.""" assert await async_setup_component(hass, 'websocket_api', {}) + await hass.async_block_till_done() diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 1b62c5244e4024..eade9d5fc6319b 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -108,7 +108,7 @@ async def test_async_from_config_file_not_mount_deps_folder(loop): async def test_load_hassio(hass): """Test that we load Hass.io component.""" with patch.dict(os.environ, {}, clear=True): - assert bootstrap._get_components(hass, {}) == set() + assert bootstrap._get_domains(hass, {}) == set() with patch.dict(os.environ, {'HASSIO': '1'}): - assert bootstrap._get_components(hass, {}) == {'hassio'} + assert bootstrap._get_domains(hass, {}) == {'hassio'} diff --git a/tests/test_loader.py b/tests/test_loader.py index 09f830a8eab8af..e3888412f0c649 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -4,9 +4,10 @@ import pytest import homeassistant.loader as loader -import homeassistant.components.http as http +from homeassistant.components import http, hue +from homeassistant.components.hue import light as hue_light -from tests.common import MockModule, async_mock_service +from tests.common import MockModule, async_mock_service, mock_integration def test_set_component(hass): @@ -22,31 +23,30 @@ def test_get_component(hass): assert http == loader.get_component(hass, 'http') -def test_component_dependencies(hass): +async def test_component_dependencies(hass): """Test if we can get the proper load order of components.""" - loader.set_component(hass, 'mod1', MockModule('mod1')) - loader.set_component(hass, 'mod2', MockModule('mod2', ['mod1'])) - loader.set_component(hass, 'mod3', MockModule('mod3', ['mod2'])) + mock_integration(hass, MockModule('mod1')) + mock_integration(hass, MockModule('mod2', ['mod1'])) + mock_integration(hass, MockModule('mod3', ['mod2'])) assert {'mod1', 'mod2', 'mod3'} == \ - loader.component_dependencies(hass, 'mod3') + await loader.async_component_dependencies(hass, 'mod3') # Create circular dependency - loader.set_component(hass, 'mod1', MockModule('mod1', ['mod3'])) + mock_integration(hass, MockModule('mod1', ['mod3'])) with pytest.raises(loader.CircularDependency): - print(loader.component_dependencies(hass, 'mod3')) + print(await loader.async_component_dependencies(hass, 'mod3')) # Depend on non-existing component - loader.set_component(hass, 'mod1', - MockModule('mod1', ['nonexisting'])) + mock_integration(hass, MockModule('mod1', ['nonexisting'])) - with pytest.raises(loader.ComponentNotFound): - print(loader.component_dependencies(hass, 'mod1')) + with pytest.raises(loader.IntegrationNotFound): + print(await loader.async_component_dependencies(hass, 'mod1')) # Try to get dependencies for non-existing component - with pytest.raises(loader.ComponentNotFound): - print(loader.component_dependencies(hass, 'nonexisting')) + with pytest.raises(loader.IntegrationNotFound): + print(await loader.async_component_dependencies(hass, 'nonexisting')) def test_component_loader(hass): @@ -142,3 +142,39 @@ async def test_get_platform_enforces_component_path(hass, caplog): assert loader.get_platform(hass, 'comp_path_test', 'hue') is None assert ('Search path was limited to path of component: ' 'homeassistant.components') in caplog.text + + +async def test_get_integration(hass): + """Test resolving integration.""" + integration = await loader.async_get_integration(hass, 'hue') + assert hue == integration.get_component() + assert hue_light == integration.get_platform('light') + + +async def test_get_integration_legacy(hass): + """Test resolving integration.""" + integration = await loader.async_get_integration(hass, 'test_embedded') + assert integration.get_component().DOMAIN == 'test_embedded' + assert integration.get_platform('switch') is not None + + +async def test_get_integration_custom_component(hass): + """Test resolving integration.""" + integration = await loader.async_get_integration(hass, 'test_package') + print(integration) + assert integration.get_component().DOMAIN == 'test_package' + assert integration.name == 'Test Package' + + +def test_integration_properties(hass): + """Test integration properties.""" + integration = loader.Integration(hass, 'homeassistant.components.hue', { + 'name': 'Philips Hue', + 'domain': 'hue', + 'dependencies': ['test-dep'], + 'requirements': ['test-req==1.0.0'], + }) + assert integration.name == "Philips Hue" + assert integration.domain == 'hue' + assert integration.dependencies == ['test-dep'] + assert integration.requirements == ['test-req==1.0.0'] diff --git a/tests/test_setup.py b/tests/test_setup.py index c6126bc4a3bbfd..00518776b52aa4 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -20,7 +20,7 @@ from tests.common import \ get_test_home_assistant, MockModule, MockPlatform, \ - assert_setup_component, get_test_config_dir + assert_setup_component, get_test_config_dir, mock_integration ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE VERSION_PATH = os.path.join(get_test_config_dir(), config_util.VERSION_FILE) @@ -378,18 +378,18 @@ def setup_component(): def test_component_not_setup_missing_dependencies(self): """Test we do not set up a component if not all dependencies loaded.""" - deps = ['non_existing'] - loader.set_component( - self.hass, 'comp', MockModule('comp', dependencies=deps)) + deps = ['maybe_existing'] + mock_integration(self.hass, MockModule('comp', dependencies=deps)) assert not setup.setup_component(self.hass, 'comp', {}) assert 'comp' not in self.hass.config.components self.hass.data.pop(setup.DATA_SETUP) - loader.set_component( - self.hass, 'non_existing', MockModule('non_existing')) - assert setup.setup_component(self.hass, 'comp', {}) + mock_integration(self.hass, MockModule('comp2', dependencies=deps)) + mock_integration(self.hass, MockModule('maybe_existing')) + + assert setup.setup_component(self.hass, 'comp2', {}) def test_component_failing_setup(self): """Test component that fails setup.""" diff --git a/tests/testing_config/custom_components/test_embedded/__init__.py b/tests/testing_config/custom_components/test_embedded/__init__.py index 2206054356e996..21843fc927a925 100644 --- a/tests/testing_config/custom_components/test_embedded/__init__.py +++ b/tests/testing_config/custom_components/test_embedded/__init__.py @@ -1,4 +1,5 @@ """Component with embedded platforms.""" +DOMAIN = 'test_embedded' async def async_setup(hass, config): diff --git a/tests/testing_config/custom_components/test_package/manifest.json b/tests/testing_config/custom_components/test_package/manifest.json new file mode 100644 index 00000000000000..320d2768d27029 --- /dev/null +++ b/tests/testing_config/custom_components/test_package/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "test_package", + "name": "Test Package", + "documentation": "http://test-package.io", + "requirements": [], + "dependencies": [], + "codeowners": [] +} From 4803f319b6e6e71bed45e0535ba108a6ee584e3e Mon Sep 17 00:00:00 2001 From: Justin Vanderhooft Date: Tue, 9 Apr 2019 13:00:50 -0400 Subject: [PATCH 532/605] bump raincloudy to 0.0.7 (#22935) --- homeassistant/components/raincloud/__init__.py | 2 +- homeassistant/components/raincloud/manifest.json | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py index c31729197be3fe..c94315f673d9ec 100644 --- a/homeassistant/components/raincloud/__init__.py +++ b/homeassistant/components/raincloud/__init__.py @@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['raincloudy==0.0.6'] +REQUIREMENTS = ['raincloudy==0.0.7'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/raincloud/manifest.json b/homeassistant/components/raincloud/manifest.json index dddfc6f156ae34..4d07f2a3ce4c6e 100644 --- a/homeassistant/components/raincloud/manifest.json +++ b/homeassistant/components/raincloud/manifest.json @@ -3,7 +3,7 @@ "name": "Raincloud", "documentation": "https://www.home-assistant.io/components/raincloud", "requirements": [ - "raincloudy==0.0.6" + "raincloudy==0.0.7" ], "dependencies": [], "codeowners": ["@vanstinator"] diff --git a/requirements_all.txt b/requirements_all.txt index 8c92d73445f2b2..3ef9230b2393ed 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1474,7 +1474,7 @@ rachiopy==0.1.3 radiotherm==2.0.0 # homeassistant.components.raincloud -raincloudy==0.0.6 +raincloudy==0.0.7 # homeassistant.components.raspihats # raspihats==2.2.3 From 58ec77b017b9fa4daee1dcc76135c4f08fa3b310 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 9 Apr 2019 20:28:20 +0200 Subject: [PATCH 533/605] Binary sensors for netgear_lte (#22902) * Binary sensors for netgear_lte * Move LTEEntity to component * Revert unrelated manifest changes * Address review comments * Remove unused import --- .../components/netgear_lte/__init__.py | 75 ++++++++++++++++++- .../components/netgear_lte/binary_sensor.py | 47 ++++++++++++ .../components/netgear_lte/manifest.json | 2 +- .../components/netgear_lte/sensor.py | 49 +----------- .../components/netgear_lte/sensor_types.py | 19 +++-- requirements_all.txt | 2 +- 6 files changed, 136 insertions(+), 58 deletions(-) create mode 100644 homeassistant/components/netgear_lte/binary_sensor.py diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index c611c65797de99..c0f248a3dd5205 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -11,16 +11,20 @@ CONF_HOST, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_PASSWORD, CONF_RECIPIENT, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback +from homeassistant.components.binary_sensor import ( + DOMAIN as BINARY_SENSOR_DOMAIN) from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_create_clientsession -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_send, async_dispatcher_connect) +from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_time_interval from . import sensor_types -REQUIREMENTS = ['eternalegypt==0.0.6'] +REQUIREMENTS = ['eternalegypt==0.0.7'] _LOGGER = logging.getLogger(__name__) @@ -47,8 +51,15 @@ }) SENSOR_SCHEMA = vol.Schema({ - vol.Optional(CONF_MONITORED_CONDITIONS, default=sensor_types.DEFAULT): - vol.All(cv.ensure_list, [vol.In(sensor_types.ALL)]), + vol.Optional(CONF_MONITORED_CONDITIONS, + default=sensor_types.DEFAULT_SENSORS): + vol.All(cv.ensure_list, [vol.In(sensor_types.ALL_SENSORS)]), +}) + +BINARY_SENSOR_SCHEMA = vol.Schema({ + vol.Optional(CONF_MONITORED_CONDITIONS, + default=sensor_types.DEFAULT_BINARY_SENSORS): + vol.All(cv.ensure_list, [vol.In(sensor_types.ALL_BINARY_SENSORS)]), }) CONFIG_SCHEMA = vol.Schema({ @@ -59,6 +70,8 @@ vol.All(cv.ensure_list, [NOTIFY_SCHEMA]), vol.Optional(SENSOR_DOMAIN, default={}): SENSOR_SCHEMA, + vol.Optional(BINARY_SENSOR_DOMAIN, default={}): + BINARY_SENSOR_SCHEMA, })]) }, extra=vol.ALLOW_EXTRA) @@ -160,6 +173,15 @@ async def delete_sms_handler(service): hass.async_create_task(discovery.async_load_platform( hass, SENSOR_DOMAIN, DOMAIN, discovery_info, config)) + # Binary Sensor + binary_sensor_conf = lte_conf.get(BINARY_SENSOR_DOMAIN) + discovery_info = { + CONF_HOST: lte_conf[CONF_HOST], + BINARY_SENSOR_DOMAIN: binary_sensor_conf, + } + hass.async_create_task(discovery.async_load_platform( + hass, BINARY_SENSOR_DOMAIN, DOMAIN, discovery_info, config)) + return True @@ -239,3 +261,48 @@ async def _retry_login(hass, modem_data, password): await _login(hass, modem_data, password) except eternalegypt.Error: delay = min(2*delay, 300) + + +@attr.s +class LTEEntity(Entity): + """Base LTE entity.""" + + modem_data = attr.ib() + sensor_type = attr.ib() + + _unique_id = attr.ib(init=False) + + @_unique_id.default + def _init_unique_id(self): + """Register unique_id while we know data is valid.""" + return "{}_{}".format( + self.sensor_type, self.modem_data.data.serial_number) + + async def async_added_to_hass(self): + """Register callback.""" + async_dispatcher_connect( + self.hass, DISPATCHER_NETGEAR_LTE, self.async_write_ha_state) + + async def async_update(self): + """Force update of state.""" + await self.modem_data.async_update() + + @property + def should_poll(self): + """Return that the sensor should not be polled.""" + return False + + @property + def available(self): + """Return the availability of the sensor.""" + return self.modem_data.data is not None + + @property + def unique_id(self): + """Return a unique ID like 'usage_5TG365AB0078V'.""" + return self._unique_id + + @property + def name(self): + """Return the name of the sensor.""" + return "Netgear LTE {}".format(self.sensor_type) diff --git a/homeassistant/components/netgear_lte/binary_sensor.py b/homeassistant/components/netgear_lte/binary_sensor.py new file mode 100644 index 00000000000000..a26c8538ea5be9 --- /dev/null +++ b/homeassistant/components/netgear_lte/binary_sensor.py @@ -0,0 +1,47 @@ +"""Support for Netgear LTE binary sensors.""" +import logging + +from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice +from homeassistant.exceptions import PlatformNotReady + +from . import CONF_MONITORED_CONDITIONS, DATA_KEY, LTEEntity +from .sensor_types import BINARY_SENSOR_CLASSES + +DEPENDENCIES = ['netgear_lte'] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info): + """Set up Netgear LTE binary sensor devices.""" + if discovery_info is None: + return + + modem_data = hass.data[DATA_KEY].get_modem_data(discovery_info) + + if not modem_data or not modem_data.data: + raise PlatformNotReady + + binary_sensor_conf = discovery_info[DOMAIN] + monitored_conditions = binary_sensor_conf[CONF_MONITORED_CONDITIONS] + + binary_sensors = [] + for sensor_type in monitored_conditions: + binary_sensors.append(LTEBinarySensor(modem_data, sensor_type)) + + async_add_entities(binary_sensors) + + +class LTEBinarySensor(LTEEntity, BinarySensorDevice): + """Netgear LTE binary sensor entity.""" + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return getattr(self.modem_data.data, self.sensor_type) + + @property + def device_class(self): + """Return the class of binary sensor.""" + return BINARY_SENSOR_CLASSES[self.sensor_type] diff --git a/homeassistant/components/netgear_lte/manifest.json b/homeassistant/components/netgear_lte/manifest.json index c35895c8c0f401..1ec50755d0428b 100644 --- a/homeassistant/components/netgear_lte/manifest.json +++ b/homeassistant/components/netgear_lte/manifest.json @@ -3,7 +3,7 @@ "name": "Netgear lte", "documentation": "https://www.home-assistant.io/components/netgear_lte", "requirements": [ - "eternalegypt==0.0.6" + "eternalegypt==0.0.7" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 8141444bfc4af0..611fcbdafa7026 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -1,14 +1,10 @@ """Support for Netgear LTE sensors.""" import logging -import attr - from homeassistant.components.sensor import DOMAIN from homeassistant.exceptions import PlatformNotReady -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from . import CONF_MONITORED_CONDITIONS, DATA_KEY, DISPATCHER_NETGEAR_LTE +from . import CONF_MONITORED_CONDITIONS, DATA_KEY, LTEEntity from .sensor_types import SENSOR_SMS, SENSOR_USAGE, SENSOR_UNITS DEPENDENCIES = ['netgear_lte'] @@ -42,50 +38,9 @@ async def async_setup_platform( async_add_entities(sensors) -@attr.s -class LTESensor(Entity): +class LTESensor(LTEEntity): """Base LTE sensor entity.""" - modem_data = attr.ib() - sensor_type = attr.ib() - - _unique_id = attr.ib(init=False) - - @_unique_id.default - def _init_unique_id(self): - """Register unique_id while we know data is valid.""" - return "{}_{}".format( - self.sensor_type, self.modem_data.data.serial_number) - - async def async_added_to_hass(self): - """Register callback.""" - async_dispatcher_connect( - self.hass, DISPATCHER_NETGEAR_LTE, self.async_write_ha_state) - - async def async_update(self): - """Force update of state.""" - await self.modem_data.async_update() - - @property - def should_poll(self): - """Return that the sensor should not be polled.""" - return False - - @property - def available(self): - """Return the availability of the sensor.""" - return self.modem_data.data is not None - - @property - def unique_id(self): - """Return a unique ID like 'usage_5TG365AB0078V'.""" - return self._unique_id - - @property - def name(self): - """Return the name of the sensor.""" - return "Netgear LTE {}".format(self.sensor_type) - @property def unit_of_measurement(self): """Return the unit of measurement.""" diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py index 5a56404abda18d..5b29a20d56b485 100644 --- a/homeassistant/components/netgear_lte/sensor_types.py +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -1,5 +1,7 @@ """Define possible sensor types.""" +from homeassistant.components.binary_sensor import DEVICE_CLASS_CONNECTIVITY + SENSOR_SMS = 'sms' SENSOR_USAGE = 'usage' @@ -10,17 +12,24 @@ 'rx_level': 'dBm', 'tx_level': 'dBm', 'upstream': None, - 'wire_connected': None, - 'mobile_connected': None, 'connection_text': None, 'connection_type': None, 'current_ps_service_type': None, 'register_network_display': None, - 'roaming': None, 'current_band': None, 'cell_id': None, } -ALL = list(SENSOR_UNITS) +BINARY_SENSOR_MOBILE_CONNECTED = 'mobile_connected' + +BINARY_SENSOR_CLASSES = { + 'roaming': None, + 'wire_connected': DEVICE_CLASS_CONNECTIVITY, + BINARY_SENSOR_MOBILE_CONNECTED: DEVICE_CLASS_CONNECTIVITY, +} + +ALL_SENSORS = list(SENSOR_UNITS) +DEFAULT_SENSORS = [SENSOR_USAGE] -DEFAULT = [SENSOR_USAGE] +ALL_BINARY_SENSORS = list(BINARY_SENSOR_CLASSES) +DEFAULT_BINARY_SENSORS = [BINARY_SENSOR_MOBILE_CONNECTED] diff --git a/requirements_all.txt b/requirements_all.txt index 3ef9230b2393ed..307e76d64a54c7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -398,7 +398,7 @@ ephem==3.7.6.0 epson-projector==0.1.3 # homeassistant.components.netgear_lte -eternalegypt==0.0.6 +eternalegypt==0.0.7 # homeassistant.components.keyboard_remote # evdev==0.6.1 From c82d2cb11cf575bddb8ca4f417a7230418ddb0e6 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Tue, 9 Apr 2019 13:59:15 -0700 Subject: [PATCH 534/605] Cherry pick test fix (#22939) --- tests/common.py | 16 +++++++---- .../automation/test_numeric_state.py | 4 +-- tests/components/automation/test_state.py | 28 +++++++++++++++---- tests/components/automation/test_template.py | 2 +- tests/components/automation/test_time.py | 2 +- tests/components/demo/test_notify.py | 2 +- tests/components/group/test_notify.py | 2 +- tests/components/rflink/test_init.py | 2 ++ tests/test_config_entries.py | 2 +- 9 files changed, 41 insertions(+), 19 deletions(-) diff --git a/tests/common.py b/tests/common.py index 2f89d91e4d3c40..962e98b24e705d 100644 --- a/tests/common.py +++ b/tests/common.py @@ -444,6 +444,7 @@ def __init__(self, domain=None, dependencies=None, setup=None, async_migrate_entry=None, async_remove_entry=None): """Initialize the mock module.""" self.__name__ = 'homeassistant.components.{}'.format(domain) + self.__file__ = 'homeassistant/components/{}'.format(domain) self.DOMAIN = domain self.DEPENDENCIES = dependencies or [] self.REQUIREMENTS = requirements or [] @@ -483,7 +484,8 @@ def __init__(self, domain=None, dependencies=None, setup=None, class MockPlatform: """Provide a fake platform.""" - __name__ = "homeassistant.components.light.bla" + __name__ = 'homeassistant.components.light.bla' + __file__ = 'homeassistant/components/blah/light' # pylint: disable=invalid-name def __init__(self, setup_platform=None, dependencies=None, @@ -694,13 +696,15 @@ def assert_setup_component(count, domain=None): config = {} @ha.callback - def mock_psc(hass, config_input, domain): + def mock_psc(hass, config_input, domain_input): """Mock the prepare_setup_component to capture config.""" res = async_process_component_config( - hass, config_input, domain) - config[domain] = None if res is None else res.get(domain) + hass, config_input, domain_input) + config[domain_input] = None if res is None else res.get(domain_input) _LOGGER.debug("Configuration for %s, Validated: %s, Original %s", - domain, config[domain], config_input.get(domain)) + domain_input, + config[domain_input], + config_input.get(domain_input)) return res assert isinstance(config, dict) @@ -709,7 +713,7 @@ def mock_psc(hass, config_input, domain): yield config if domain is None: - assert len(config) >= 1, ('assert_setup_component requires DOMAIN: {}' + assert len(config) == 1, ('assert_setup_component requires DOMAIN: {}' .format(list(config.keys()))) domain = list(config.keys())[0] diff --git a/tests/components/automation/test_numeric_state.py b/tests/components/automation/test_numeric_state.py index 803a15e9634f65..86a1a3daff5fbe 100644 --- a/tests/components/automation/test_numeric_state.py +++ b/tests/components/automation/test_numeric_state.py @@ -703,7 +703,7 @@ async def test_if_action(hass, calls): async def test_if_fails_setup_bad_for(hass, calls): """Test for setup failure for bad for.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -723,7 +723,7 @@ async def test_if_fails_setup_bad_for(hass, calls): async def test_if_fails_setup_for_without_above_below(hass, calls): """Test for setup failures for missing above or below.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index 53c1eaab3d99d9..4ce695afeb9c1a 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -51,6 +51,7 @@ async def test_if_fires_on_entity_change(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world', context=context) await hass.async_block_till_done() @@ -80,6 +81,7 @@ async def test_if_fires_on_entity_change_with_from_filter(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -100,6 +102,7 @@ async def test_if_fires_on_entity_change_with_to_filter(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -120,6 +123,7 @@ async def test_if_fires_on_attribute_change_with_to_filter(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world', {'test_attribute': 11}) hass.states.async_set('test.entity', 'world', {'test_attribute': 12}) @@ -142,6 +146,7 @@ async def test_if_fires_on_entity_change_with_both_filters(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -163,6 +168,7 @@ async def test_if_not_fires_if_to_filter_not_match(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'moon') await hass.async_block_till_done() @@ -186,6 +192,7 @@ async def test_if_not_fires_if_from_filter_not_match(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -205,6 +212,7 @@ async def test_if_not_fires_if_entity_not_match(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -231,6 +239,7 @@ async def test_if_action(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set(entity_id, test_state) hass.bus.async_fire('test_event') @@ -247,7 +256,7 @@ async def test_if_action(hass, calls): async def test_if_fails_setup_if_to_boolean_value(hass, calls): """Test for setup failure for boolean to.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -263,7 +272,7 @@ async def test_if_fails_setup_if_to_boolean_value(hass, calls): async def test_if_fails_setup_if_from_boolean_value(hass, calls): """Test for setup failure for boolean from.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -279,7 +288,7 @@ async def test_if_fails_setup_if_from_boolean_value(hass, calls): async def test_if_fails_setup_bad_for(hass, calls): """Test for setup failure for bad for.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -298,7 +307,7 @@ async def test_if_fails_setup_bad_for(hass, calls): async def test_if_fails_setup_for_without_to(hass, calls): """Test for setup failures for missing to.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -331,6 +340,7 @@ async def test_if_not_fires_on_entity_change_with_for(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -362,6 +372,7 @@ async def test_if_not_fires_on_entities_change_with_for_after_stop(hass, } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity_1', 'world') hass.states.async_set('test.entity_2', 'world') @@ -402,6 +413,7 @@ async def test_if_fires_on_entity_change_with_for_attribute_change(hass, } } }) + await hass.async_block_till_done() utcnow = dt_util.utcnow() with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: @@ -438,6 +450,7 @@ async def test_if_fires_on_entity_change_with_for_multiple_force_update(hass, } } }) + await hass.async_block_till_done() utcnow = dt_util.utcnow() with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: @@ -473,6 +486,7 @@ async def test_if_fires_on_entity_change_with_for(hass, calls): } } }) + await hass.async_block_till_done() hass.states.async_set('test.entity', 'world') await hass.async_block_till_done() @@ -505,6 +519,7 @@ async def test_if_fires_on_for_condition(hass, calls): 'action': {'service': 'test.automation'}, } }) + await hass.async_block_till_done() # not enough time has passed hass.bus.async_fire('test_event') @@ -543,6 +558,7 @@ async def test_if_fires_on_for_condition_attribute_change(hass, calls): 'action': {'service': 'test.automation'}, } }) + await hass.async_block_till_done() # not enough time has passed hass.bus.async_fire('test_event') @@ -566,7 +582,7 @@ async def test_if_fires_on_for_condition_attribute_change(hass, calls): async def test_if_fails_setup_for_without_time(hass, calls): """Test for setup failure if no time is provided.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { @@ -585,7 +601,7 @@ async def test_if_fails_setup_for_without_time(hass, calls): async def test_if_fails_setup_for_without_entity(hass, calls): """Test for setup failure if no entity is provided.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': {'event_type': 'bla'}, diff --git a/tests/components/automation/test_template.py b/tests/components/automation/test_template.py index f803f97f4abd99..25f32ac193942f 100644 --- a/tests/components/automation/test_template.py +++ b/tests/components/automation/test_template.py @@ -369,7 +369,7 @@ async def test_if_action(hass, calls): async def test_if_fires_on_change_with_bad_template(hass, calls): """Test for firing on change with bad template.""" - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { diff --git a/tests/components/automation/test_time.py b/tests/components/automation/test_time.py index 0e570800342300..cc5ef302d817ad 100644 --- a/tests/components/automation/test_time.py +++ b/tests/components/automation/test_time.py @@ -56,7 +56,7 @@ async def test_if_not_fires_using_wrong_at(hass, calls): This should break the before rule. """ - with assert_setup_component(0): + with assert_setup_component(0, automation.DOMAIN): assert await async_setup_component(hass, automation.DOMAIN, { automation.DOMAIN: { 'trigger': { diff --git a/tests/components/demo/test_notify.py b/tests/components/demo/test_notify.py index 964612cb977953..cd3ae52a7cc58d 100644 --- a/tests/components/demo/test_notify.py +++ b/tests/components/demo/test_notify.py @@ -42,7 +42,7 @@ def tearDown(self): # pylint: disable=invalid-name self.hass.stop() def _setup_notify(self): - with assert_setup_component(1) as config: + with assert_setup_component(1, notify.DOMAIN) as config: assert setup_component(self.hass, notify.DOMAIN, CONFIG) assert config[notify.DOMAIN] self.hass.block_till_done() diff --git a/tests/components/group/test_notify.py b/tests/components/group/test_notify.py index 9412e9f95a4b07..72993c9006be63 100644 --- a/tests/components/group/test_notify.py +++ b/tests/components/group/test_notify.py @@ -29,7 +29,7 @@ def mock_get_service(hass, config, discovery_info=None): return self.service1 return self.service2 - with assert_setup_component(2), \ + with assert_setup_component(2, notify.DOMAIN), \ patch.object(demo, 'get_service', mock_get_service): setup_component(self.hass, notify.DOMAIN, { 'notify': [{ diff --git a/tests/components/rflink/test_init.py b/tests/components/rflink/test_init.py index 46cbef92aa492f..69d5049902bb57 100644 --- a/tests/components/rflink/test_init.py +++ b/tests/components/rflink/test_init.py @@ -42,7 +42,9 @@ async def create_rflink_connection(*args, **kwargs): 'rflink.protocol.create_rflink_connection', mock_create) + await async_setup_component(hass, 'rflink', config) await async_setup_component(hass, domain, config) + await hass.async_block_till_done() # hook into mock config for injecting events event_callback = mock_create.call_args_list[0][1]['event_callback'] diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 32532761ccf9f0..fc8541a096a486 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -162,7 +162,7 @@ async def test_remove_entry(hass, manager): """Test that we can remove an entry.""" async def mock_setup_entry(hass, entry): """Mock setting up entry.""" - hass.loop.create_task(hass.config_entries.async_forward_entry_setup( + hass.async_create_task(hass.config_entries.async_forward_entry_setup( entry, 'light')) return True From 6244a397b1f0e83c649dec537c70d4e2d45db420 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 9 Apr 2019 23:09:18 +0200 Subject: [PATCH 535/605] Hide unsupported Sonos favorites (#22940) --- homeassistant/components/sonos/__init__.py | 2 +- homeassistant/components/sonos/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 9c9914b787b854..d5f89cd074ed0d 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -4,7 +4,7 @@ DOMAIN = 'sonos' -REQUIREMENTS = ['pysonos==0.0.9'] +REQUIREMENTS = ['pysonos==0.0.10'] async def async_setup(hass, config): diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index 79cc76539378fe..b2598bc5be92a0 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,7 +3,7 @@ "name": "Sonos", "documentation": "https://www.home-assistant.io/components/sonos", "requirements": [ - "pysonos==0.0.9" + "pysonos==0.0.10" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 307e76d64a54c7..563e1a7bc8fd79 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1273,7 +1273,7 @@ pysmartthings==0.6.7 pysnmp==4.4.8 # homeassistant.components.sonos -pysonos==0.0.9 +pysonos==0.0.10 # homeassistant.components.spc pyspcwebgw==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6e70c636314a08..c1bb31edeb6f25 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -239,7 +239,7 @@ pysmartapp==0.3.2 pysmartthings==0.6.7 # homeassistant.components.sonos -pysonos==0.0.9 +pysonos==0.0.10 # homeassistant.components.spc pyspcwebgw==0.4.0 From c4e31bc4dfe42a476447644d68a7aa3037aa777e Mon Sep 17 00:00:00 2001 From: Austin Drummond Date: Tue, 9 Apr 2019 17:13:48 -0400 Subject: [PATCH 536/605] Add linked battery sensor to HomeKit (#22788) --- .../components/homekit/accessories.py | 36 +++++++++--- homeassistant/components/homekit/const.py | 1 + homeassistant/components/homekit/util.py | 11 ++-- tests/components/homekit/test_accessories.py | 57 ++++++++++++++++++- tests/components/homekit/test_util.py | 14 ++++- 5 files changed, 103 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index 2738fafbfdb1f1..8b0e70f616e96c 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -19,8 +19,8 @@ from .const import ( ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_SERIAL_NUMBER, CHAR_BATTERY_LEVEL, CHAR_CHARGING_STATE, CHAR_STATUS_LOW_BATTERY, - DEBOUNCE_TIMEOUT, EVENT_HOMEKIT_CHANGED, MANUFACTURER, - SERV_BATTERY_SERVICE) + CONF_LINKED_BATTERY_SENSOR, DEBOUNCE_TIMEOUT, EVENT_HOMEKIT_CHANGED, + MANUFACTURER, SERV_BATTERY_SERVICE) from .util import convert_to_float, dismiss_setup_message, show_setup_message _LOGGER = logging.getLogger(__name__) @@ -65,19 +65,25 @@ def __init__(self, hass, driver, name, entity_id, aid, config, firmware_revision=__version__, manufacturer=MANUFACTURER, model=model, serial_number=entity_id) self.category = category - self.config = config + self.config = config or {} self.entity_id = entity_id self.hass = hass self.debounce = {} self._support_battery_level = False self._support_battery_charging = True + self.linked_battery_sensor = \ + self.config.get(CONF_LINKED_BATTERY_SENSOR) """Add battery service if available""" - battery_level = self.hass.states.get(self.entity_id).attributes \ + battery_found = self.hass.states.get(self.entity_id).attributes \ .get(ATTR_BATTERY_LEVEL) - if battery_level is None: + if self.linked_battery_sensor: + battery_found = self.hass.states.get( + self.linked_battery_sensor).state + + if battery_found is None: return - _LOGGER.debug('%s: Found battery level attribute', self.entity_id) + _LOGGER.debug('%s: Found battery level', self.entity_id) self._support_battery_level = True serv_battery = self.add_preload_service(SERV_BATTERY_SERVICE) self._char_battery = serv_battery.configure_char( @@ -104,6 +110,14 @@ async def run_handler(self): async_track_state_change( self.hass, self.entity_id, self.update_state_callback) + if self.linked_battery_sensor: + battery_state = self.hass.states.get(self.linked_battery_sensor) + self.hass.async_add_job(self.update_linked_battery, None, None, + battery_state) + async_track_state_change( + self.hass, self.linked_battery_sensor, + self.update_linked_battery) + @ha_callback def update_state_callback(self, entity_id=None, old_state=None, new_state=None): @@ -111,10 +125,16 @@ def update_state_callback(self, entity_id=None, old_state=None, _LOGGER.debug('New_state: %s', new_state) if new_state is None: return - if self._support_battery_level: + if self._support_battery_level and not self.linked_battery_sensor: self.hass.async_add_executor_job(self.update_battery, new_state) self.hass.async_add_executor_job(self.update_state, new_state) + @ha_callback + def update_linked_battery(self, entity_id=None, old_state=None, + new_state=None): + """Handle linked battery sensor state change listener callback.""" + self.hass.async_add_executor_job(self.update_battery, new_state) + def update_battery(self, new_state): """Update battery service if available. @@ -122,6 +142,8 @@ def update_battery(self, new_state): """ battery_level = convert_to_float( new_state.attributes.get(ATTR_BATTERY_LEVEL)) + if self.linked_battery_sensor: + battery_level = convert_to_float(new_state.state) if battery_level is None: return self._char_battery.set_value(battery_level) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 1b2a4dbf05d2a9..4a96f0add8d92a 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -15,6 +15,7 @@ CONF_FEATURE = 'feature' CONF_FEATURE_LIST = 'feature_list' CONF_FILTER = 'filter' +CONF_LINKED_BATTERY_SENSOR = 'linked_battery_sensor' CONF_SAFE_MODE = 'safe_mode' # #### Config Defaults #### diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 2ba5819a202d49..cf113e3ffe2b26 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -4,7 +4,7 @@ import voluptuous as vol -from homeassistant.components import fan, media_player +from homeassistant.components import fan, media_player, sensor from homeassistant.const import ( ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_NAME, CONF_TYPE, TEMP_CELSIUS) from homeassistant.core import split_entity_id @@ -12,22 +12,23 @@ import homeassistant.util.temperature as temp_util from .const import ( - CONF_FEATURE, CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, - FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE, HOMEKIT_NOTIFY_ID, TYPE_FAUCET, - TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_SWITCH, TYPE_VALVE) + CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR, + FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE, + HOMEKIT_NOTIFY_ID, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, + TYPE_SWITCH, TYPE_VALVE) _LOGGER = logging.getLogger(__name__) BASIC_INFO_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_LINKED_BATTERY_SENSOR): cv.entity_domain(sensor.DOMAIN), }) FEATURE_SCHEMA = BASIC_INFO_SCHEMA.extend({ vol.Optional(CONF_FEATURE_LIST, default=None): cv.ensure_list, }) - CODE_SCHEMA = BASIC_INFO_SCHEMA.extend({ vol.Optional(ATTR_CODE, default=None): vol.Any(None, cv.string), }) diff --git a/tests/components/homekit/test_accessories.py b/tests/components/homekit/test_accessories.py index 6f3957827eb63d..c9798f6302a6cd 100644 --- a/tests/components/homekit/test_accessories.py +++ b/tests/components/homekit/test_accessories.py @@ -13,7 +13,7 @@ ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER, CHAR_FIRMWARE_REVISION, CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER, - MANUFACTURER, SERV_ACCESSORY_INFO) + CONF_LINKED_BATTERY_SENSOR, MANUFACTURER, SERV_ACCESSORY_INFO) from homeassistant.const import ( __version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID, ATTR_SERVICE, ATTR_NOW, EVENT_TIME_CHANGED) @@ -156,6 +156,61 @@ async def test_battery_service(hass, hk_driver, caplog): assert acc._char_charging.value == 0 +async def test_linked_battery_sensor(hass, hk_driver, caplog): + """Test battery service with linked_battery_sensor.""" + entity_id = 'homekit.accessory' + linked_battery = 'sensor.battery' + hass.states.async_set(entity_id, 'open', {ATTR_BATTERY_LEVEL: 100}) + hass.states.async_set(linked_battery, 50, None) + await hass.async_block_till_done() + + acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2, + {CONF_LINKED_BATTERY_SENSOR: linked_battery}) + acc.update_state = lambda x: None + assert acc.linked_battery_sensor == linked_battery + + await hass.async_add_job(acc.run) + await hass.async_block_till_done() + assert acc._char_battery.value == 50 + assert acc._char_low_battery.value == 0 + assert acc._char_charging.value == 2 + + hass.states.async_set(linked_battery, 10, None) + await hass.async_block_till_done() + assert acc._char_battery.value == 10 + assert acc._char_low_battery.value == 1 + + # Ignore battery change on entity if it has linked_battery + hass.states.async_set(entity_id, 'open', {ATTR_BATTERY_LEVEL: 90}) + await hass.async_block_till_done() + assert acc._char_battery.value == 10 + + # Test none numeric state for linked_battery + hass.states.async_set(linked_battery, 'error', None) + await hass.async_block_till_done() + assert acc._char_battery.value == 10 + assert 'ERROR' not in caplog.text + + # Test charging + hass.states.async_set(linked_battery, 20, {ATTR_BATTERY_CHARGING: True}) + await hass.async_block_till_done() + + acc = HomeAccessory(hass, hk_driver, 'Battery Service', entity_id, 2, + {CONF_LINKED_BATTERY_SENSOR: linked_battery}) + acc.update_state = lambda x: None + await hass.async_add_job(acc.run) + await hass.async_block_till_done() + assert acc._char_battery.value == 20 + assert acc._char_low_battery.value == 0 + assert acc._char_charging.value == 1 + + hass.states.async_set(linked_battery, 100, {ATTR_BATTERY_CHARGING: False}) + await hass.async_block_till_done() + assert acc._char_battery.value == 100 + assert acc._char_low_battery.value == 0 + assert acc._char_charging.value == 0 + + async def test_call_service(hass, hk_driver, events): """Test call_service method.""" entity_id = 'homekit.accessory' diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index c86b1353c48936..635b35cad51314 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -3,9 +3,9 @@ import voluptuous as vol from homeassistant.components.homekit.const import ( - CONF_FEATURE, CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, - HOMEKIT_NOTIFY_ID, TYPE_FAUCET, TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, - TYPE_SWITCH, TYPE_VALVE) + CONF_FEATURE, CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR, + FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, HOMEKIT_NOTIFY_ID, TYPE_FAUCET, + TYPE_OUTLET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_SWITCH, TYPE_VALVE) from homeassistant.components.homekit.util import ( HomeKitSpeedMapping, SpeedRange, convert_to_float, density_to_air_quality, dismiss_setup_message, show_setup_message, temperature_to_homekit, @@ -25,6 +25,9 @@ def test_validate_entity_config(): """Test validate entities.""" configs = [None, [], 'string', 12345, {'invalid_entity_id': {}}, {'demo.test': 1}, + {'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: None}}, + {'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: + 'switch.demo'}}, {'demo.test': 'test'}, {'demo.test': [1, 2]}, {'demo.test': None}, {'demo.test': {CONF_NAME: None}}, {'media_player.test': {CONF_FEATURE_LIST: [ @@ -42,6 +45,11 @@ def test_validate_entity_config(): assert vec({'demo.test': {CONF_NAME: 'Name'}}) == \ {'demo.test': {CONF_NAME: 'Name'}} + assert vec({'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: + 'sensor.demo_battery'}}) == \ + {'binary_sensor.demo': {CONF_LINKED_BATTERY_SENSOR: + 'sensor.demo_battery'}} + assert vec({'alarm_control_panel.demo': {}}) == \ {'alarm_control_panel.demo': {ATTR_CODE: None}} assert vec({'alarm_control_panel.demo': {ATTR_CODE: '1234'}}) == \ From 8582e390f837fa554da02c14b72c197b6ff1496a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 9 Apr 2019 14:45:09 -0700 Subject: [PATCH 537/605] Remove introduction component (#22944) * Remove introduction component * Remove more usage --- CODEOWNERS | 1 - homeassistant/bootstrap.py | 1 - homeassistant/components/demo/__init__.py | 2 +- homeassistant/components/demo/manifest.json | 1 - .../components/introduction/__init__.py | 56 ------------------- .../components/introduction/manifest.json | 10 ---- homeassistant/config.py | 3 - tests/components/introduction/__init__.py | 1 - tests/components/introduction/test_init.py | 23 -------- 9 files changed, 1 insertion(+), 97 deletions(-) delete mode 100644 homeassistant/components/introduction/__init__.py delete mode 100644 homeassistant/components/introduction/manifest.json delete mode 100644 tests/components/introduction/__init__.py delete mode 100644 tests/components/introduction/test_init.py diff --git a/CODEOWNERS b/CODEOWNERS index 033f8331ebf788..2a0548a4ded3b2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -109,7 +109,6 @@ homeassistant/components/input_number/* @home-assistant/core homeassistant/components/input_select/* @home-assistant/core homeassistant/components/input_text/* @home-assistant/core homeassistant/components/integration/* @dgomes -homeassistant/components/introduction/* @home-assistant/core homeassistant/components/ios/* @robbiet480 homeassistant/components/ipma/* @dgomes homeassistant/components/irish_rail_transport/* @ttroy50 diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index c27dde9f9408c3..a8c6d7732914c6 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -35,7 +35,6 @@ 'recorder', 'mqtt', 'mqtt_eventstream', - 'introduction', 'frontend', 'history', } diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 2699ade0b1d005..70c1341145de8c 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -6,7 +6,7 @@ import homeassistant.core as ha from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM -DEPENDENCIES = ['conversation', 'introduction', 'zone'] +DEPENDENCIES = ['conversation', 'zone'] DOMAIN = 'demo' COMPONENTS_WITH_DEMO_PLATFORM = [ diff --git a/homeassistant/components/demo/manifest.json b/homeassistant/components/demo/manifest.json index 859cac597dc276..ab079a4c2eed1d 100644 --- a/homeassistant/components/demo/manifest.json +++ b/homeassistant/components/demo/manifest.json @@ -5,7 +5,6 @@ "requirements": [], "dependencies": [ "conversation", - "introduction", "zone" ], "codeowners": [ diff --git a/homeassistant/components/introduction/__init__.py b/homeassistant/components/introduction/__init__.py deleted file mode 100644 index 8a2d72ebbddaf0..00000000000000 --- a/homeassistant/components/introduction/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Component that will help guide the user taking its first steps.""" -import logging - -import voluptuous as vol - -DOMAIN = 'introduction' - -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({}), -}, extra=vol.ALLOW_EXTRA) - - -async def async_setup(hass, config=None): - """Set up the introduction component.""" - log = logging.getLogger(__name__) - log.info(""" - - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Hello, and welcome to Home Assistant! - - We'll hope that we can make all your dreams come true. - - Here are some resources to get started: - - - Configuring Home Assistant: - https://home-assistant.io/getting-started/configuration/ - - - Available components: - https://home-assistant.io/components/ - - - Troubleshooting your configuration: - https://home-assistant.io/getting-started/troubleshooting-configuration/ - - - Getting help: - https://home-assistant.io/help/ - - This message is generated by the introduction component. You can - disable it in configuration.yaml. - - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - """) - - hass.components.persistent_notification.async_create(""" -Here are some resources to get started: - - - [Configuring Home Assistant](https://home-assistant.io/getting-started/configuration/) - - [Available components](https://home-assistant.io/components/) - - [Troubleshooting your configuration](https://home-assistant.io/docs/configuration/troubleshooting/) - - [Getting help](https://home-assistant.io/help/) - -To not see this card popup in the future, edit your config in -`configuration.yaml` and disable the `introduction` component. -""", 'Welcome Home!') # noqa - - return True diff --git a/homeassistant/components/introduction/manifest.json b/homeassistant/components/introduction/manifest.json deleted file mode 100644 index 4caa31e34e6127..00000000000000 --- a/homeassistant/components/introduction/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "domain": "introduction", - "name": "Introduction", - "documentation": "https://www.home-assistant.io/components/introduction", - "requirements": [], - "dependencies": [], - "codeowners": [ - "@home-assistant/core" - ] -} diff --git a/homeassistant/config.py b/homeassistant/config.py index 19b8087e538dfd..fe7f904a4b5796 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -68,9 +68,6 @@ # Configure a default setup of Home Assistant (frontend, api, etc) default_config: -# Show the introduction message on startup. -introduction: - # Uncomment this if you are using SSL/TLS, running in Docker container, etc. # http: # base_url: example.duckdns.org:8123 diff --git a/tests/components/introduction/__init__.py b/tests/components/introduction/__init__.py deleted file mode 100644 index 99cea29581c7cd..00000000000000 --- a/tests/components/introduction/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the introduction component.""" diff --git a/tests/components/introduction/test_init.py b/tests/components/introduction/test_init.py deleted file mode 100644 index c414ab97ae18d1..00000000000000 --- a/tests/components/introduction/test_init.py +++ /dev/null @@ -1,23 +0,0 @@ -"""The tests for the Introduction component.""" -import unittest - -from homeassistant.setup import setup_component -from homeassistant.components import introduction - -from tests.common import get_test_home_assistant - - -class TestIntroduction(unittest.TestCase): - """Test Introduction.""" - - def setUp(self): - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - def tearDown(self): - """Stop down everything that was started.""" - self.hass.stop() - - def test_setup(self): - """Test introduction setup.""" - assert setup_component(self.hass, introduction.DOMAIN, {}) From e48ef7f44187587f8f65201117e02f8fd2382894 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 9 Apr 2019 15:42:44 -0700 Subject: [PATCH 538/605] Fix broken platform components (#22943) * Fix broken platform components * Lint --- .../components/automation/__init__.py | 8 ++----- homeassistant/components/mqtt/__init__.py | 22 ++++------------- homeassistant/components/mqtt/const.py | 3 +++ homeassistant/components/mqtt/discovery.py | 2 +- .../components/telegram_bot/__init__.py | 11 ++++----- .../components/telegram_bot/manifest.json | 2 +- homeassistant/loader.py | 24 ++++--------------- tests/components/automation/test_webhook.py | 9 +++++++ 8 files changed, 30 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 94776deefb0e23..b1470582d59232 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -18,7 +18,6 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.loader import bind_hass -from homeassistant.setup import async_prepare_setup_platform from homeassistant.util.dt import utcnow DOMAIN = 'automation' @@ -416,11 +415,8 @@ async def _async_process_trigger(hass, config, trigger_configs, name, action): } for conf in trigger_configs: - platform = await async_prepare_setup_platform( - hass, config, DOMAIN, conf.get(CONF_PLATFORM)) - - if platform is None: - return None + platform = importlib.import_module('.{}'.format(conf[CONF_PLATFORM]), + __name__) remove = await platform.async_trigger(hass, conf, action, info) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 9af0465c0de306..4f9ad990105853 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -30,15 +30,15 @@ from homeassistant.helpers.typing import ( ConfigType, HomeAssistantType, ServiceDataType) from homeassistant.loader import bind_hass -from homeassistant.setup import async_prepare_setup_platform from homeassistant.util.async_ import ( run_callback_threadsafe, run_coroutine_threadsafe) from homeassistant.util.logging import catch_log_exception # Loading the config flow file will register the flow -from . import config_flow # noqa pylint: disable=unused-import -from .const import CONF_BROKER, CONF_DISCOVERY, DEFAULT_DISCOVERY -from .server import HBMQTT_CONFIG_SCHEMA +from . import config_flow, discovery, server # noqa pylint: disable=unused-import +from .const import ( + CONF_BROKER, CONF_DISCOVERY, DEFAULT_DISCOVERY, CONF_STATE_TOPIC, + ATTR_DISCOVERY_HASH) REQUIREMENTS = ['paho-mqtt==1.4.0'] @@ -66,7 +66,6 @@ CONF_BIRTH_MESSAGE = 'birth_message' CONF_WILL_MESSAGE = 'will_message' -CONF_STATE_TOPIC = 'state_topic' CONF_COMMAND_TOPIC = 'command_topic' CONF_AVAILABILITY_TOPIC = 'availability_topic' CONF_PAYLOAD_AVAILABLE = 'payload_available' @@ -101,7 +100,6 @@ ATTR_PAYLOAD_TEMPLATE = 'payload_template' ATTR_QOS = CONF_QOS ATTR_RETAIN = CONF_RETAIN -ATTR_DISCOVERY_HASH = 'discovery_hash' MAX_RECONNECT_WAIT = 300 # seconds @@ -209,7 +207,7 @@ def embedded_broker_deprecated(value): vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])), vol.Optional(CONF_EMBEDDED): - vol.All(HBMQTT_CONFIG_SCHEMA, embedded_broker_deprecated), + vol.All(server.HBMQTT_CONFIG_SCHEMA, embedded_broker_deprecated), vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean, @@ -408,13 +406,6 @@ async def _async_setup_server(hass: HomeAssistantType, config: ConfigType): """ conf = config.get(DOMAIN, {}) # type: ConfigType - server = await async_prepare_setup_platform( - hass, config, DOMAIN, 'server') - - if server is None: - _LOGGER.error("Unable to load embedded server") - return None - success, broker_config = \ await server.async_start( hass, conf.get(CONF_PASSWORD), conf.get(CONF_EMBEDDED)) @@ -432,9 +423,6 @@ async def _async_setup_discovery(hass: HomeAssistantType, conf: ConfigType, This method is a coroutine. """ - discovery = await async_prepare_setup_platform( - hass, hass_config, DOMAIN, 'discovery') - if discovery is None: _LOGGER.error("Unable to load MQTT discovery") return False diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 3c22001f91ce46..42b1a5b6755664 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -2,3 +2,6 @@ CONF_BROKER = 'broker' CONF_DISCOVERY = 'discovery' DEFAULT_DISCOVERY = False + +ATTR_DISCOVERY_HASH = 'discovery_hash' +CONF_STATE_TOPIC = 'state_topic' diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index b10e05cbf0f262..20b707eec1761c 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -10,7 +10,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import HomeAssistantType -from . import ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC +from .const import ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index e8b19ec2b2c0cb..7d19e8212b6aca 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -1,6 +1,7 @@ """Support to send and receive Telegram messages.""" import io from functools import partial +import importlib import logging import requests @@ -14,7 +15,6 @@ CONF_PLATFORM, CONF_TIMEOUT, HTTP_DIGEST_AUTHENTICATION) import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import TemplateError -from homeassistant.setup import async_prepare_setup_platform REQUIREMENTS = ['python-telegram-bot==11.1.0'] @@ -76,7 +76,7 @@ PARSER_MD = 'markdown' PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ - vol.Required(CONF_PLATFORM): cv.string, + vol.Required(CONF_PLATFORM): vol.In(('broadcast', 'polling', 'webhooks')), vol.Required(CONF_API_KEY): cv.string, vol.Required(CONF_ALLOWED_CHAT_IDS): vol.All(cv.ensure_list, [vol.Coerce(int)]), @@ -219,11 +219,8 @@ async def async_setup(hass, config): p_type = p_config.get(CONF_PLATFORM) - platform = await async_prepare_setup_platform( - hass, config, DOMAIN, p_type) - - if platform is None: - return + platform = importlib.import_module('.{}'.format(config[CONF_PLATFORM]), + __name__) _LOGGER.info("Setting up %s.%s", DOMAIN, p_type) try: diff --git a/homeassistant/components/telegram_bot/manifest.json b/homeassistant/components/telegram_bot/manifest.json index ba52cd4e935d50..f341fd587ca0e1 100644 --- a/homeassistant/components/telegram_bot/manifest.json +++ b/homeassistant/components/telegram_bot/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "python-telegram-bot==11.1.0" ], - "dependencies": [], + "dependencies": ["http"], "codeowners": [] } diff --git a/homeassistant/loader.py b/homeassistant/loader.py index a1dbad3439ee4d..17f0130da4dbdd 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -49,7 +49,6 @@ PACKAGE_CUSTOM_COMPONENTS = 'custom_components' PACKAGE_BUILTIN = 'homeassistant.components' LOOKUP_PATHS = [PACKAGE_CUSTOM_COMPONENTS, PACKAGE_BUILTIN] -COMPONENTS_WITH_BAD_PLATFORMS = ['automation', 'mqtt', 'telegram_bot'] _UNDEF = object() @@ -224,11 +223,7 @@ def get_platform(hass, # type: HomeAssistant """ # If the platform has a component, we will limit the platform loading path # to be the same source (custom/built-in). - if domain not in COMPONENTS_WITH_BAD_PLATFORMS: - component = _load_file(hass, platform_name, LOOKUP_PATHS) - else: - # avoid load component for legacy platform - component = None + component = _load_file(hass, platform_name, LOOKUP_PATHS) # Until we have moved all platforms under their component/own folder, it # can be that the component is None. @@ -244,14 +239,6 @@ def get_platform(hass, # type: HomeAssistant if platform is not None: return platform - # Legacy platform check for automation: components/automation/event.py - if component is None and domain in COMPONENTS_WITH_BAD_PLATFORMS: - platform = _load_file( - hass, - PLATFORM_FORMAT.format(domain=platform_name, platform=domain), - base_paths - ) - # Legacy platform check for custom: custom_components/light/hue.py # Only check if the component was also in custom components. if component is None or base_paths[0] == PACKAGE_CUSTOM_COMPONENTS: @@ -270,11 +257,10 @@ def get_platform(hass, # type: HomeAssistant _LOGGER.error("Unable to find platform %s.%s", platform_name, extra) return None - if domain not in COMPONENTS_WITH_BAD_PLATFORMS: - _LOGGER.error( - "Integrations need to be in their own folder. Change %s/%s.py to " - "%s/%s.py. This will stop working soon.", - domain, platform_name, platform_name, domain) + _LOGGER.error( + "Integrations need to be in their own folder. Change %s/%s.py to " + "%s/%s.py. This will stop working soon.", + domain, platform_name, platform_name, domain) return platform diff --git a/tests/components/automation/test_webhook.py b/tests/components/automation/test_webhook.py index a6cde3955839ff..c42f3805662c61 100644 --- a/tests/components/automation/test_webhook.py +++ b/tests/components/automation/test_webhook.py @@ -1,8 +1,17 @@ """The tests for the webhook automation trigger.""" +import pytest + from homeassistant.core import callback from homeassistant.setup import async_setup_component +@pytest.fixture(autouse=True) +async def setup_http(hass): + """Set up http.""" + assert await async_setup_component(hass, 'http', {}) + assert await async_setup_component(hass, 'webhook', {}) + + async def test_webhook_json(hass, aiohttp_client): """Test triggering with a JSON webhook.""" events = [] From 37f3eccb1e7cef8316490c00df87d94c1edc3346 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 10 Apr 2019 05:13:39 +0200 Subject: [PATCH 539/605] Bugfix: pass protocol out of header to application layer (#22955) --- homeassistant/components/hassio/ingress.py | 41 +++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 4f7c99618c19d4..f4cc97c385392e 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -70,7 +70,18 @@ async def _handle_websocket( self, request: web.Request, token: str, path: str ) -> web.WebSocketResponse: """Ingress route for websocket.""" - ws_server = web.WebSocketResponse() + if hdrs.SEC_WEBSOCKET_PROTOCOL in request.headers: + req_protocols = [ + str(proto.strip()) + for proto in + request.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + else: + req_protocols = () + + ws_server = web.WebSocketResponse( + protocols=req_protocols, autoclose=False, autoping=False + ) await ws_server.prepare(request) # Preparing @@ -83,7 +94,8 @@ async def _handle_websocket( # Start proxy async with self._websession.ws_connect( - url, headers=source_header + url, headers=source_header, protocols=req_protocols, + autoclose=False, autoping=False, ) as ws_client: # Proxy requests await asyncio.wait( @@ -205,14 +217,17 @@ def _is_websocket(request: web.Request) -> bool: async def _websocket_forward(ws_from, ws_to): """Handle websocket message directly.""" - async for msg in ws_from: - if msg.type == aiohttp.WSMsgType.TEXT: - await ws_to.send_str(msg.data) - elif msg.type == aiohttp.WSMsgType.BINARY: - await ws_to.send_bytes(msg.data) - elif msg.type == aiohttp.WSMsgType.PING: - await ws_to.ping() - elif msg.type == aiohttp.WSMsgType.PONG: - await ws_to.pong() - elif ws_to.closed: - await ws_to.close(code=ws_to.close_code, message=msg.extra) + try: + async for msg in ws_from: + if msg.type == aiohttp.WSMsgType.TEXT: + await ws_to.send_str(msg.data) + elif msg.type == aiohttp.WSMsgType.BINARY: + await ws_to.send_bytes(msg.data) + elif msg.type == aiohttp.WSMsgType.PING: + await ws_to.ping() + elif msg.type == aiohttp.WSMsgType.PONG: + await ws_to.pong() + elif ws_to.closed: + await ws_to.close(code=ws_to.close_code, message=msg.extra) + except RuntimeError: + _LOGGER.debug("Ingress Websocket runtime error") From 51e6d5380e5bfa5b030aee5493c11415815a796e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 9 Apr 2019 20:17:13 -0700 Subject: [PATCH 540/605] Add color setting trait (#22894) --- .../components/google_assistant/trait.py | 174 ++++++++---------- tests/components/google_assistant/__init__.py | 9 +- .../google_assistant/test_smart_home.py | 6 +- .../components/google_assistant/test_trait.py | 45 ++--- 4 files changed, 97 insertions(+), 137 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 41549c021fe547..124649982117e8 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -43,8 +43,7 @@ TRAIT_DOCK = PREFIX_TRAITS + 'Dock' TRAIT_STARTSTOP = PREFIX_TRAITS + 'StartStop' TRAIT_BRIGHTNESS = PREFIX_TRAITS + 'Brightness' -TRAIT_COLOR_SPECTRUM = PREFIX_TRAITS + 'ColorSpectrum' -TRAIT_COLOR_TEMP = PREFIX_TRAITS + 'ColorTemperature' +TRAIT_COLOR_SETTING = PREFIX_TRAITS + 'ColorSetting' TRAIT_SCENE = PREFIX_TRAITS + 'Scene' TRAIT_TEMPERATURE_SETTING = PREFIX_TRAITS + 'TemperatureSetting' TRAIT_LOCKUNLOCK = PREFIX_TRAITS + 'LockUnlock' @@ -274,69 +273,13 @@ async def execute(self, command, data, params): @register_trait -class ColorSpectrumTrait(_Trait): - """Trait to offer color spectrum functionality. - - https://developers.google.com/actions/smarthome/traits/colorspectrum - """ - - name = TRAIT_COLOR_SPECTRUM - commands = [ - COMMAND_COLOR_ABSOLUTE - ] - - @staticmethod - def supported(domain, features, device_class): - """Test if state is supported.""" - if domain != light.DOMAIN: - return False - - return features & light.SUPPORT_COLOR - - def sync_attributes(self): - """Return color spectrum attributes for a sync request.""" - # Other colorModel is hsv - return {'colorModel': 'rgb'} - - def query_attributes(self): - """Return color spectrum query attributes.""" - response = {} - - color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) - if color_hs is not None: - response['color'] = { - 'spectrumRGB': int(color_util.color_rgb_to_hex( - *color_util.color_hs_to_RGB(*color_hs)), 16), - } - - return response - - def can_execute(self, command, params): - """Test if command can be executed.""" - return (command in self.commands and - 'spectrumRGB' in params.get('color', {})) - - async def execute(self, command, data, params): - """Execute a color spectrum command.""" - # Convert integer to hex format and left pad with 0's till length 6 - hex_value = "{0:06x}".format(params['color']['spectrumRGB']) - color = color_util.color_RGB_to_hs( - *color_util.rgb_hex_to_rgb_list(hex_value)) - - await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: self.state.entity_id, - light.ATTR_HS_COLOR: color - }, blocking=True, context=data.context) - - -@register_trait -class ColorTemperatureTrait(_Trait): +class ColorSettingTrait(_Trait): """Trait to offer color temperature functionality. https://developers.google.com/actions/smarthome/traits/colortemperature """ - name = TRAIT_COLOR_TEMP + name = TRAIT_COLOR_SETTING commands = [ COMMAND_COLOR_ABSOLUTE ] @@ -347,59 +290,92 @@ def supported(domain, features, device_class): if domain != light.DOMAIN: return False - return features & light.SUPPORT_COLOR_TEMP + return (features & light.SUPPORT_COLOR_TEMP or + features & light.SUPPORT_COLOR) def sync_attributes(self): """Return color temperature attributes for a sync request.""" attrs = self.state.attributes - # Max Kelvin is Min Mireds K = 1000000 / mireds - # Min Kevin is Max Mireds K = 1000000 / mireds - return { - 'temperatureMaxK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MIN_MIREDS)), - 'temperatureMinK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MAX_MIREDS)), - } + features = attrs.get(ATTR_SUPPORTED_FEATURES, 0) + response = {} + + if features & light.SUPPORT_COLOR: + response['colorModel'] = 'rgb' + + if features & light.SUPPORT_COLOR_TEMP: + # Max Kelvin is Min Mireds K = 1000000 / mireds + # Min Kevin is Max Mireds K = 1000000 / mireds + response['temperatureMaxK'] = \ + color_util.color_temperature_mired_to_kelvin( + attrs.get(light.ATTR_MIN_MIREDS)) + response['temperatureMinK'] = \ + color_util.color_temperature_mired_to_kelvin( + attrs.get(light.ATTR_MAX_MIREDS)) + + return response def query_attributes(self): """Return color temperature query attributes.""" - response = {} + features = self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + color = {} + + if features & light.SUPPORT_COLOR: + color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) + if color_hs is not None: + color['spectrumRGB'] = int( + color_util.color_rgb_to_hex( + *color_util.color_hs_to_RGB(*color_hs)), + 16 + ) - temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) - # Some faulty integrations might put 0 in here, raising exception. - if temp == 0: - _LOGGER.warning('Entity %s has incorrect color temperature %s', - self.state.entity_id, temp) - elif temp is not None: - response['color'] = { - 'temperature': + if features & light.SUPPORT_COLOR_TEMP: + temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) + # Some faulty integrations might put 0 in here, raising exception. + if temp == 0: + _LOGGER.warning('Entity %s has incorrect color temperature %s', + self.state.entity_id, temp) + elif temp is not None: + color['temperature'] = \ color_util.color_temperature_mired_to_kelvin(temp) - } - return response + response = {} - def can_execute(self, command, params): - """Test if command can be executed.""" - return (command in self.commands and - 'temperature' in params.get('color', {})) + if color: + response['color'] = color + + return response async def execute(self, command, data, params): """Execute a color temperature command.""" - temp = color_util.color_temperature_kelvin_to_mired( - params['color']['temperature']) - min_temp = self.state.attributes[light.ATTR_MIN_MIREDS] - max_temp = self.state.attributes[light.ATTR_MAX_MIREDS] - - if temp < min_temp or temp > max_temp: - raise SmartHomeError( - ERR_VALUE_OUT_OF_RANGE, - "Temperature should be between {} and {}".format(min_temp, - max_temp)) - - await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: self.state.entity_id, - light.ATTR_COLOR_TEMP: temp, - }, blocking=True, context=data.context) + if 'temperature' in params['color']: + temp = color_util.color_temperature_kelvin_to_mired( + params['color']['temperature']) + min_temp = self.state.attributes[light.ATTR_MIN_MIREDS] + max_temp = self.state.attributes[light.ATTR_MAX_MIREDS] + + if temp < min_temp or temp > max_temp: + raise SmartHomeError( + ERR_VALUE_OUT_OF_RANGE, + "Temperature should be between {} and {}".format(min_temp, + max_temp)) + + await self.hass.services.async_call( + light.DOMAIN, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id, + light.ATTR_COLOR_TEMP: temp, + }, blocking=True, context=data.context) + + elif 'spectrumRGB' in params['color']: + # Convert integer to hex format and left pad with 0's till length 6 + hex_value = "{0:06x}".format(params['color']['spectrumRGB']) + color = color_util.color_RGB_to_hs( + *color_util.rgb_hex_to_rgb_list(hex_value)) + + await self.hass.services.async_call( + light.DOMAIN, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id, + light.ATTR_HS_COLOR: color + }, blocking=True, context=data.context) @register_trait diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index 331c6d2d9f5082..898bc04fbd9e61 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -10,8 +10,7 @@ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', @@ -50,8 +49,7 @@ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', @@ -65,8 +63,7 @@ }, 'traits': [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSpectrum', - 'action.devices.traits.ColorTemperature' + 'action.devices.traits.ColorSetting', ], 'type': 'action.devices.types.LIGHT', diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index bc59cc8ff2d7c0..d33c1f1bc5b65c 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -93,8 +93,7 @@ async def test_sync_message(hass): 'traits': [ trait.TRAIT_BRIGHTNESS, trait.TRAIT_ONOFF, - trait.TRAIT_COLOR_SPECTRUM, - trait.TRAIT_COLOR_TEMP, + trait.TRAIT_COLOR_SETTING, ], 'type': sh.TYPE_LIGHT, 'willReportState': False, @@ -172,8 +171,7 @@ async def test_sync_in_area(hass, registries): 'traits': [ trait.TRAIT_BRIGHTNESS, trait.TRAIT_ONOFF, - trait.TRAIT_COLOR_SPECTRUM, - trait.TRAIT_COLOR_TEMP, + trait.TRAIT_COLOR_SETTING, ], 'type': sh.TYPE_LIGHT, 'willReportState': False, diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index d85fc692cb9751..4a84d789068629 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -452,14 +452,15 @@ async def test_startstop_vacuum(hass): } -async def test_color_spectrum_light(hass): +async def test_color_setting_color_light(hass): """Test ColorSpectrum trait support for light domain.""" - assert not trait.ColorSpectrumTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorSpectrumTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR, None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR, None) - trt = trait.ColorSpectrumTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_HS_COLOR: (0, 94), + ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR, }), BASIC_CONFIG) assert trt.sync_attributes() == { @@ -472,11 +473,6 @@ async def test_color_spectrum_light(hass): } } - assert not trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { - 'color': { - 'temperature': 400 - } - }) assert trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { 'color': { 'spectrumRGB': 16715792 @@ -496,17 +492,17 @@ async def test_color_spectrum_light(hass): } -async def test_color_temperature_light(hass): +async def test_color_setting_temperature_light(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP, - None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR_TEMP, None) - trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, light.ATTR_COLOR_TEMP: 300, light.ATTR_MAX_MIREDS: 500, + ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR_TEMP, }), BASIC_CONFIG) assert trt.sync_attributes() == { @@ -525,12 +521,6 @@ async def test_color_temperature_light(hass): 'temperature': 400 } }) - assert not trt.can_execute(trait.COMMAND_COLOR_ABSOLUTE, { - 'color': { - 'spectrumRGB': 16715792 - } - }) - calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) with pytest.raises(helpers.SmartHomeError) as err: @@ -553,14 +543,13 @@ async def test_color_temperature_light(hass): } -async def test_color_temperature_light_bad_temp(hass): +async def test_color_light_temperature_light_bad_temp(hass): """Test ColorTemperature trait support for light domain.""" - assert not trait.ColorTemperatureTrait.supported(light.DOMAIN, 0, None) - assert trait.ColorTemperatureTrait.supported(light.DOMAIN, - light.SUPPORT_COLOR_TEMP, - None) + assert not trait.ColorSettingTrait.supported(light.DOMAIN, 0, None) + assert trait.ColorSettingTrait.supported(light.DOMAIN, + light.SUPPORT_COLOR_TEMP, None) - trt = trait.ColorTemperatureTrait(hass, State('light.bla', STATE_ON, { + trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { light.ATTR_MIN_MIREDS: 200, light.ATTR_COLOR_TEMP: 0, light.ATTR_MAX_MIREDS: 500, From 6d2412022b86080e3990079bfc5b78afa4d644d5 Mon Sep 17 00:00:00 2001 From: cdce8p <30130371+cdce8p@users.noreply.github.com> Date: Wed, 10 Apr 2019 08:35:17 +0200 Subject: [PATCH 541/605] Fix HomeKit fan speed conversion (#22951) * Check that speed value from state is not 'None' * Added tests --- homeassistant/components/homekit/util.py | 2 ++ tests/components/homekit/test_util.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index cf113e3ffe2b26..1bf57f1b1f9b58 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -148,6 +148,8 @@ def __init__(self, speed_list): def speed_to_homekit(self, speed): """Map Home Assistant speed state to HomeKit speed.""" + if speed is None: + return None speed_range = self.speed_ranges[speed] return speed_range.target diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 635b35cad51314..9ffcfe5c01e561 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -191,6 +191,7 @@ def test_homekit_speed_mapping(): def test_speed_to_homekit(): """Test speed conversion from HA to Homekit.""" speed_mapping = HomeKitSpeedMapping(['off', 'low', 'high']) + assert speed_mapping.speed_to_homekit(None) is None assert speed_mapping.speed_to_homekit('off') == 0 assert speed_mapping.speed_to_homekit('low') == 50 assert speed_mapping.speed_to_homekit('high') == 100 @@ -199,6 +200,7 @@ def test_speed_to_homekit(): def test_speed_to_states(): """Test speed conversion from Homekit to HA.""" speed_mapping = HomeKitSpeedMapping(['off', 'low', 'high']) + assert speed_mapping.speed_to_states(-1) == 'off' assert speed_mapping.speed_to_states(0) == 'off' assert speed_mapping.speed_to_states(33) == 'off' assert speed_mapping.speed_to_states(34) == 'low' From a833736a1e5417ad18702c1557bb0a51d5a03a14 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Wed, 10 Apr 2019 09:41:57 +0200 Subject: [PATCH 542/605] Add sms_total sensor to netgear_lte (#22954) --- homeassistant/components/netgear_lte/sensor.py | 18 +++++++++++++++--- .../components/netgear_lte/sensor_types.py | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 611fcbdafa7026..238a5f9b72d3d6 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -5,7 +5,8 @@ from homeassistant.exceptions import PlatformNotReady from . import CONF_MONITORED_CONDITIONS, DATA_KEY, LTEEntity -from .sensor_types import SENSOR_SMS, SENSOR_USAGE, SENSOR_UNITS +from .sensor_types import ( + SENSOR_SMS, SENSOR_SMS_TOTAL, SENSOR_USAGE, SENSOR_UNITS) DEPENDENCIES = ['netgear_lte'] @@ -29,7 +30,9 @@ async def async_setup_platform( sensors = [] for sensor_type in monitored_conditions: if sensor_type == SENSOR_SMS: - sensors.append(SMSSensor(modem_data, sensor_type)) + sensors.append(SMSUnreadSensor(modem_data, sensor_type)) + elif sensor_type == SENSOR_SMS_TOTAL: + sensors.append(SMSTotalSensor(modem_data, sensor_type)) elif sensor_type == SENSOR_USAGE: sensors.append(UsageSensor(modem_data, sensor_type)) else: @@ -47,7 +50,7 @@ def unit_of_measurement(self): return SENSOR_UNITS[self.sensor_type] -class SMSSensor(LTESensor): +class SMSUnreadSensor(LTESensor): """Unread SMS sensor entity.""" @property @@ -56,6 +59,15 @@ def state(self): return sum(1 for x in self.modem_data.data.sms if x.unread) +class SMSTotalSensor(LTESensor): + """Total SMS sensor entity.""" + + @property + def state(self): + """Return the state of the sensor.""" + return len(self.modem_data.data.sms) + + class UsageSensor(LTESensor): """Data usage sensor entity.""" diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py index 5b29a20d56b485..3171c1d9663f80 100644 --- a/homeassistant/components/netgear_lte/sensor_types.py +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -3,10 +3,12 @@ from homeassistant.components.binary_sensor import DEVICE_CLASS_CONNECTIVITY SENSOR_SMS = 'sms' +SENSOR_SMS_TOTAL = 'sms_total' SENSOR_USAGE = 'usage' SENSOR_UNITS = { SENSOR_SMS: 'unread', + SENSOR_SMS_TOTAL: 'messages', SENSOR_USAGE: 'MiB', 'radio_quality': '%', 'rx_level': 'dBm', From 5d3aac8130d87f16f8ef8b1ad3587bfb00973279 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 10 Apr 2019 09:44:00 +0200 Subject: [PATCH 543/605] Use ConfigEntryNotReady when setting up Daikin (#22901) * raise ConfigEntryNotReady * better debugging --- homeassistant/components/daikin/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 2df831eb6dbed5..8e96ccb87388a9 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -9,6 +9,7 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_HOST, CONF_HOSTS +from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.typing import HomeAssistantType @@ -88,17 +89,17 @@ async def daikin_api_setup(hass, host): from pydaikin.appliance import Appliance session = hass.helpers.aiohttp_client.async_get_clientsession() try: - with timeout(10, loop=hass.loop): + with timeout(10): device = Appliance(host, session) await device.init() except asyncio.TimeoutError: - _LOGGER.error("Connection to Daikin timeout") - return None + _LOGGER.debug("Connection to %s timed out", host) + raise ConfigEntryNotReady except ClientConnectionError: - _LOGGER.error("ServerDisconected") - return None + _LOGGER.debug("ClientConnectionError to %s", host) + raise ConfigEntryNotReady except Exception: # pylint: disable=broad-except - _LOGGER.error("Unexpected error creating device") + _LOGGER.error("Unexpected error creating device %s", host) return None api = DaikinApi(device) From bbedf091aad8a2465c27b4358ccd5d8bd892eeae Mon Sep 17 00:00:00 2001 From: mgiako <44270368+mgiako@users.noreply.github.com> Date: Wed, 10 Apr 2019 09:55:39 +0200 Subject: [PATCH 544/605] Add functionality to the version sensor (#22896) * Update manifest.json * Update sensor.py * new version option --- homeassistant/components/version/manifest.json | 2 +- homeassistant/components/version/sensor.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/version/manifest.json b/homeassistant/components/version/manifest.json index 34f984f953a893..5684a3c64d1426 100644 --- a/homeassistant/components/version/manifest.json +++ b/homeassistant/components/version/manifest.json @@ -3,7 +3,7 @@ "name": "Version", "documentation": "https://www.home-assistant.io/components/version", "requirements": [ - "pyhaversion==2.0.3" + "pyhaversion==2.2.0" ], "dependencies": [], "codeowners": [ diff --git a/homeassistant/components/version/sensor.py b/homeassistant/components/version/sensor.py index 6982b77a51a9f5..7c8f2b1662a151 100644 --- a/homeassistant/components/version/sensor.py +++ b/homeassistant/components/version/sensor.py @@ -11,7 +11,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pyhaversion==2.0.3'] +REQUIREMENTS = ['pyhaversion==2.2.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 563e1a7bc8fd79..b19bbc3951c90d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1064,7 +1064,7 @@ pygtfs==0.1.5 pygtt==1.1.2 # homeassistant.components.version -pyhaversion==2.0.3 +pyhaversion==2.2.0 # homeassistant.components.heos pyheos==0.3.1 From bc5f0ff0b34fb8d12b29eeb6fb59c9aeb739e298 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 10 Apr 2019 11:16:41 +0200 Subject: [PATCH 545/605] Use dict[key] for required config keys and keys with default values of MQTT light (#22834) * Use dict[key] for required config keys and keys with default values. * Improve tests * Lint * Improve tests of JSON data --- .../components/mqtt/light/schema_basic.py | 84 +++--- .../components/mqtt/light/schema_json.py | 53 ++-- .../components/mqtt/light/schema_template.py | 23 +- tests/components/mqtt/test_light_json.py | 282 +++++++++++++++++- 4 files changed, 357 insertions(+), 85 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index a985a70748599d..d5aa4480139215 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -81,6 +81,7 @@ vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic, @@ -89,7 +90,8 @@ vol.Optional(CONF_HS_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_HS_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE): + vol.In(VALUES_ON_COMMAND_TYPE), vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, @@ -98,6 +100,7 @@ vol.Optional(CONF_RGB_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_WHITE_VALUE_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_WHITE_VALUE_SCALE, default=DEFAULT_WHITE_VALUE_SCALE): vol.All(vol.Coerce(int), vol.Range(min=1)), @@ -106,9 +109,6 @@ vol.Optional(CONF_XY_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_XY_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_XY_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_ON_COMMAND_TYPE, default=DEFAULT_ON_COMMAND_TYPE): - vol.In(VALUES_ON_COMMAND_TYPE), - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -202,8 +202,8 @@ def _setup_from_config(self, config): } self._topic = topic self._payload = { - 'on': config.get(CONF_PAYLOAD_ON), - 'off': config.get(CONF_PAYLOAD_OFF), + 'on': config[CONF_PAYLOAD_ON], + 'off': config[CONF_PAYLOAD_OFF], } self._templates = { CONF_BRIGHTNESS: config.get(CONF_BRIGHTNESS_VALUE_TEMPLATE), @@ -219,7 +219,7 @@ def _setup_from_config(self, config): CONF_XY: config.get(CONF_XY_VALUE_TEMPLATE), } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic or topic[CONF_STATE_TOPIC] is None self._optimistic_rgb = \ optimistic or topic[CONF_RGB_STATE_TOPIC] is None @@ -272,7 +272,7 @@ def state_received(msg): topics[CONF_STATE_TOPIC] = { 'topic': self._topic[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} elif self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -287,7 +287,7 @@ def brightness_received(msg): device_value = float(payload) percent_bright = \ - device_value / self._config.get(CONF_BRIGHTNESS_SCALE) + device_value / self._config[CONF_BRIGHTNESS_SCALE] self._brightness = percent_bright * 255 self.async_write_ha_state() @@ -295,7 +295,7 @@ def brightness_received(msg): topics[CONF_BRIGHTNESS_STATE_TOPIC] = { 'topic': self._topic[CONF_BRIGHTNESS_STATE_TOPIC], 'msg_callback': brightness_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._brightness = 255 elif self._optimistic_brightness and last_state\ and last_state.attributes.get(ATTR_BRIGHTNESS): @@ -326,7 +326,7 @@ def rgb_received(msg): topics[CONF_RGB_STATE_TOPIC] = { 'topic': self._topic[CONF_RGB_STATE_TOPIC], 'msg_callback': rgb_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_rgb and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -350,7 +350,7 @@ def color_temp_received(msg): topics[CONF_COLOR_TEMP_STATE_TOPIC] = { 'topic': self._topic[CONF_COLOR_TEMP_STATE_TOPIC], 'msg_callback': color_temp_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._color_temp = 150 if self._optimistic_color_temp and last_state\ and last_state.attributes.get(ATTR_COLOR_TEMP): @@ -376,7 +376,7 @@ def effect_received(msg): topics[CONF_EFFECT_STATE_TOPIC] = { 'topic': self._topic[CONF_EFFECT_STATE_TOPIC], 'msg_callback': effect_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._effect = 'none' if self._optimistic_effect and last_state\ and last_state.attributes.get(ATTR_EFFECT): @@ -406,7 +406,7 @@ def hs_received(msg): topics[CONF_HS_STATE_TOPIC] = { 'topic': self._topic[CONF_HS_STATE_TOPIC], 'msg_callback': hs_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_hs and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -425,7 +425,7 @@ def white_value_received(msg): device_value = float(payload) percent_white = \ - device_value / self._config.get(CONF_WHITE_VALUE_SCALE) + device_value / self._config[CONF_WHITE_VALUE_SCALE] self._white_value = percent_white * 255 self.async_write_ha_state() @@ -433,7 +433,7 @@ def white_value_received(msg): topics[CONF_WHITE_VALUE_STATE_TOPIC] = { 'topic': self._topic[CONF_WHITE_VALUE_STATE_TOPIC], 'msg_callback': white_value_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._white_value = 255 elif self._optimistic_white_value and last_state\ and last_state.attributes.get(ATTR_WHITE_VALUE): @@ -460,7 +460,7 @@ def xy_received(msg): topics[CONF_XY_STATE_TOPIC] = { 'topic': self._topic[CONF_XY_STATE_TOPIC], 'msg_callback': xy_received, - 'qos': self._config.get(CONF_QOS)} + 'qos': self._config[CONF_QOS]} self._hs = (0, 0) if self._optimistic_xy and last_state\ and last_state.attributes.get(ATTR_HS_COLOR): @@ -513,7 +513,7 @@ def should_poll(self): @property def name(self): """Return the name of the device if any.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -572,13 +572,13 @@ async def async_turn_on(self, **kwargs): This method is a coroutine. """ should_update = False - on_command_type = self._config.get(CONF_ON_COMMAND_TYPE) + on_command_type = self._config[CONF_ON_COMMAND_TYPE] if on_command_type == 'first': mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload['on'], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload['on'], self._config[CONF_QOS], + self._config[CONF_RETAIN]) should_update = True # If brightness is being used instead of an on command, make sure @@ -616,8 +616,8 @@ async def async_turn_on(self, **kwargs): mqtt.async_publish( self.hass, self._topic[CONF_RGB_COMMAND_TOPIC], - rgb_color_str, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + rgb_color_str, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_rgb: self._hs = kwargs[ATTR_HS_COLOR] @@ -629,8 +629,8 @@ async def async_turn_on(self, **kwargs): hs_color = kwargs[ATTR_HS_COLOR] mqtt.async_publish( self.hass, self._topic[CONF_HS_COMMAND_TOPIC], - '{},{}'.format(*hs_color), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + '{},{}'.format(*hs_color), self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_hs: self._hs = kwargs[ATTR_HS_COLOR] @@ -642,8 +642,8 @@ async def async_turn_on(self, **kwargs): xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) mqtt.async_publish( self.hass, self._topic[CONF_XY_COMMAND_TOPIC], - '{},{}'.format(*xy_color), self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + '{},{}'.format(*xy_color), self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_xy: self._hs = kwargs[ATTR_HS_COLOR] @@ -652,13 +652,13 @@ async def async_turn_on(self, **kwargs): if ATTR_BRIGHTNESS in kwargs and \ self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None: percent_bright = float(kwargs[ATTR_BRIGHTNESS]) / 255 - brightness_scale = self._config.get(CONF_BRIGHTNESS_SCALE) + brightness_scale = self._config[CONF_BRIGHTNESS_SCALE] device_brightness = \ min(round(percent_bright * brightness_scale), brightness_scale) mqtt.async_publish( self.hass, self._topic[CONF_BRIGHTNESS_COMMAND_TOPIC], - device_brightness, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + device_brightness, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_brightness: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -679,8 +679,8 @@ async def async_turn_on(self, **kwargs): mqtt.async_publish( self.hass, self._topic[CONF_RGB_COMMAND_TOPIC], - rgb_color_str, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + rgb_color_str, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_brightness: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -698,8 +698,8 @@ async def async_turn_on(self, **kwargs): mqtt.async_publish( self.hass, self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC], - color_temp, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + color_temp, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_color_temp: self._color_temp = kwargs[ATTR_COLOR_TEMP] @@ -711,8 +711,8 @@ async def async_turn_on(self, **kwargs): if effect in self._config.get(CONF_EFFECT_LIST): mqtt.async_publish( self.hass, self._topic[CONF_EFFECT_COMMAND_TOPIC], - effect, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + effect, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_effect: self._effect = kwargs[ATTR_EFFECT] @@ -721,13 +721,13 @@ async def async_turn_on(self, **kwargs): if ATTR_WHITE_VALUE in kwargs and \ self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC] is not None: percent_white = float(kwargs[ATTR_WHITE_VALUE]) / 255 - white_scale = self._config.get(CONF_WHITE_VALUE_SCALE) + white_scale = self._config[CONF_WHITE_VALUE_SCALE] device_white_value = \ min(round(percent_white * white_scale), white_scale) mqtt.async_publish( self.hass, self._topic[CONF_WHITE_VALUE_COMMAND_TOPIC], - device_white_value, self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + device_white_value, self._config[CONF_QOS], + self._config[CONF_RETAIN]) if self._optimistic_white_value: self._white_value = kwargs[ATTR_WHITE_VALUE] @@ -735,8 +735,8 @@ async def async_turn_on(self, **kwargs): if on_command_type == 'last': mqtt.async_publish(self.hass, self._topic[CONF_COMMAND_TOPIC], - self._payload['on'], self._config.get(CONF_QOS), - self._config.get(CONF_RETAIN)) + self._payload['on'], self._config[CONF_QOS], + self._config[CONF_RETAIN]) should_update = True if self._optimistic: @@ -754,7 +754,7 @@ async def async_turn_off(self, **kwargs): """ mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], self._payload['off'], - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 12f688afbf7fde..a52f3c58d0efc9 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -62,25 +62,24 @@ vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE): vol.All(vol.Coerce(int), vol.Range(min=1)), vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(CONF_FLASH_TIME_SHORT, default=DEFAULT_FLASH_TIME_SHORT): - cv.positive_int, vol.Optional(CONF_FLASH_TIME_LONG, default=DEFAULT_FLASH_TIME_LONG): cv.positive_int, + vol.Optional(CONF_FLASH_TIME_SHORT, default=DEFAULT_FLASH_TIME_SHORT): + cv.positive_int, + vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): vol.All(vol.Coerce(int), vol.In([0, 1, 2])), vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, vol.Optional(CONF_RGB, default=DEFAULT_RGB): cv.boolean, vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_WHITE_VALUE, default=DEFAULT_WHITE_VALUE): cv.boolean, vol.Optional(CONF_XY, default=DEFAULT_XY): cv.boolean, - vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean, - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -148,34 +147,34 @@ def _setup_from_config(self, config): CONF_COMMAND_TOPIC ) } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic or self._topic[CONF_STATE_TOPIC] is None - brightness = config.get(CONF_BRIGHTNESS) + brightness = config[CONF_BRIGHTNESS] if brightness: self._brightness = 255 else: self._brightness = None - color_temp = config.get(CONF_COLOR_TEMP) + color_temp = config[CONF_COLOR_TEMP] if color_temp: self._color_temp = 150 else: self._color_temp = None - effect = config.get(CONF_EFFECT) + effect = config[CONF_EFFECT] if effect: self._effect = 'none' else: self._effect = None - white_value = config.get(CONF_WHITE_VALUE) + white_value = config[CONF_WHITE_VALUE] if white_value: self._white_value = 255 else: self._white_value = None - if config.get(CONF_HS) or config.get(CONF_RGB) or config.get(CONF_XY): + if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]: self._hs = [0, 0] else: self._hs = None @@ -188,13 +187,13 @@ def _setup_from_config(self, config): } self._supported_features = (SUPPORT_TRANSITION | SUPPORT_FLASH) - self._supported_features |= (config.get(CONF_RGB) and SUPPORT_COLOR) + self._supported_features |= (config[CONF_RGB] and SUPPORT_COLOR) self._supported_features |= (brightness and SUPPORT_BRIGHTNESS) self._supported_features |= (color_temp and SUPPORT_COLOR_TEMP) self._supported_features |= (effect and SUPPORT_EFFECT) self._supported_features |= (white_value and SUPPORT_WHITE_VALUE) - self._supported_features |= (config.get(CONF_XY) and SUPPORT_COLOR) - self._supported_features |= (config.get(CONF_HS) and SUPPORT_COLOR) + self._supported_features |= (config[CONF_XY] and SUPPORT_COLOR) + self._supported_features |= (config[CONF_HS] and SUPPORT_COLOR) async def _subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -246,7 +245,7 @@ def state_received(msg): try: self._brightness = int( values['brightness'] / - float(self._config.get(CONF_BRIGHTNESS_SCALE)) * 255) + float(self._config[CONF_BRIGHTNESS_SCALE]) * 255) except KeyError: pass except ValueError: @@ -283,7 +282,7 @@ def state_received(msg): self.hass, self._sub_state, {'state_topic': {'topic': self._topic[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) if self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -343,7 +342,7 @@ def should_poll(self): @property def name(self): """Return the name of the device if any.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -375,11 +374,11 @@ async def async_turn_on(self, **kwargs): message = {'state': 'ON'} if ATTR_HS_COLOR in kwargs and ( - self._config.get(CONF_HS) or self._config.get(CONF_RGB) - or self._config.get(CONF_XY)): + self._config[CONF_HS] or self._config[CONF_RGB] + or self._config[CONF_XY]): hs_color = kwargs[ATTR_HS_COLOR] message['color'] = {} - if self._config.get(CONF_RGB): + if self._config[CONF_RGB]: # If there's a brightness topic set, we don't want to scale the # RGB values given using the brightness. if self._brightness is not None: @@ -393,11 +392,11 @@ async def async_turn_on(self, **kwargs): message['color']['r'] = rgb[0] message['color']['g'] = rgb[1] message['color']['b'] = rgb[2] - if self._config.get(CONF_XY): + if self._config[CONF_XY]: xy_color = color_util.color_hs_to_xy(*kwargs[ATTR_HS_COLOR]) message['color']['x'] = xy_color[0] message['color']['y'] = xy_color[1] - if self._config.get(CONF_HS): + if self._config[CONF_HS]: message['color']['h'] = hs_color[0] message['color']['s'] = hs_color[1] @@ -416,10 +415,10 @@ async def async_turn_on(self, **kwargs): if ATTR_TRANSITION in kwargs: message['transition'] = int(kwargs[ATTR_TRANSITION]) - if ATTR_BRIGHTNESS in kwargs: + if ATTR_BRIGHTNESS in kwargs and self._brightness is not None: message['brightness'] = int( kwargs[ATTR_BRIGHTNESS] / float(DEFAULT_BRIGHTNESS_SCALE) * - self._config.get(CONF_BRIGHTNESS_SCALE)) + self._config[CONF_BRIGHTNESS_SCALE]) if self._optimistic: self._brightness = kwargs[ATTR_BRIGHTNESS] @@ -448,7 +447,7 @@ async def async_turn_on(self, **kwargs): mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], json.dumps(message), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. @@ -470,7 +469,7 @@ async def async_turn_off(self, **kwargs): mqtt.async_publish( self.hass, self._topic[CONF_COMMAND_TOPIC], json.dumps(message), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN)) + self._config[CONF_QOS], self._config[CONF_RETAIN]) if self._optimistic: # Optimistically assume that the light has changed state. diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 27c1fb00441aca..49cba082401d16 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -51,23 +51,18 @@ vol.Optional(CONF_BLUE_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, vol.Optional(CONF_COLOR_TEMP_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template, + vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template, + vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_EFFECT_TEMPLATE): cv.template, vol.Optional(CONF_GREEN_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_RED_TEMPLATE): cv.template, - vol.Optional(CONF_RETAIN, default=mqtt.DEFAULT_RETAIN): cv.boolean, vol.Optional(CONF_STATE_TEMPLATE): cv.template, - vol.Optional(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, - vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, - vol.Required(CONF_COMMAND_OFF_TEMPLATE): cv.template, - vol.Required(CONF_COMMAND_ON_TEMPLATE): cv.template, - vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, - vol.Optional(CONF_QOS, default=mqtt.DEFAULT_QOS): - vol.All(vol.Coerce(int), vol.In([0, 1, 2])), vol.Optional(CONF_UNIQUE_ID): cv.string, - vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, + vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, }).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema).extend( mqtt.MQTT_JSON_ATTRS_SCHEMA.schema).extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) @@ -150,7 +145,7 @@ def _setup_from_config(self, config): CONF_WHITE_VALUE_TEMPLATE, ) } - optimistic = config.get(CONF_OPTIMISTIC) + optimistic = config[CONF_OPTIMISTIC] self._optimistic = optimistic \ or self._topics[CONF_STATE_TOPIC] is None \ or self._templates[CONF_STATE_TEMPLATE] is None @@ -257,7 +252,7 @@ def state_received(msg): self.hass, self._sub_state, {'state_topic': {'topic': self._topics[CONF_STATE_TOPIC], 'msg_callback': state_received, - 'qos': self._config.get(CONF_QOS)}}) + 'qos': self._config[CONF_QOS]}}) if self._optimistic and last_state: self._state = last_state.state == STATE_ON @@ -310,7 +305,7 @@ def should_poll(self): @property def name(self): """Return the name of the entity.""" - return self._config.get(CONF_NAME) + return self._config[CONF_NAME] @property def unique_id(self): @@ -396,7 +391,7 @@ async def async_turn_on(self, **kwargs): mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(**values), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN) + self._config[CONF_QOS], self._config[CONF_RETAIN] ) if self._optimistic: @@ -417,7 +412,7 @@ async def async_turn_off(self, **kwargs): mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(**values), - self._config.get(CONF_QOS), self._config.get(CONF_RETAIN) + self._config[CONF_QOS], self._config[CONF_RETAIN] ) if self._optimistic: diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 172e6dbd8cf8bf..1e325cec5abf93 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -88,6 +88,7 @@ brightness_scale: 99 """ import json +from unittest import mock from unittest.mock import ANY, patch from homeassistant.components import light, mqtt @@ -101,6 +102,19 @@ from tests.common import ( MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component, mock_coro, mock_registry) +from tests.components.light import common + + +class JsonValidator(object): + """Helper to compare JSON.""" + + def __init__(self, jsondata): + """Constructor.""" + self.jsondata = jsondata + + def __eq__(self, other): + """Compare JSON data.""" + return json.loads(self.jsondata) == json.loads(other) async def test_fail_setup_if_no_command_topic(hass, mqtt_mock): @@ -295,7 +309,9 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): 'brightness': True, 'color_temp': True, 'effect': True, + 'hs': True, 'rgb': True, + 'xy': True, 'white_value': True, 'qos': 2 } @@ -311,6 +327,65 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): assert 191 == state.attributes.get(ATTR_SUPPORTED_FEATURES) assert state.attributes.get(ATTR_ASSUMED_STATE) + common.async_turn_on(hass, 'light.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', '{"state": "ON"}', 2, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_off(hass, 'light.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', '{"state": "OFF"}', 2, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + mqtt_mock.reset_mock() + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255,' + ' "x": 0.14, "y": 0.131, "h": 210.824, "s": 100.0},' + ' "brightness": 50}'), + 2, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59,' + ' "x": 0.654, "y": 0.301, "h": 359.0, "s": 78.0},' + ' "brightness": 50}'), + 2, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0,' + ' "x": 0.611, "y": 0.375, "h": 30.118, "s": 100.0},' + ' "white_value": 80}'), + 2, False), + ], any_order=True) + + state = hass.states.get('light.test') + assert STATE_ON == state.state + assert (255, 128, 0) == state.attributes['rgb_color'] + assert 50 == state.attributes['brightness'] + assert (30.118, 100) == state.attributes['hs_color'] + assert 80 == state.attributes['white_value'] + assert (0.611, 0.375) == state.attributes['xy_color'] + async def test_sending_hs_color(hass, mqtt_mock): """Test light.turn_on with hs color sends hs color parameters.""" @@ -320,6 +395,7 @@ async def test_sending_hs_color(hass, mqtt_mock): 'schema': 'json', 'name': 'test', 'command_topic': 'test_light_rgb/set', + 'brightness': True, 'hs': True, } }) @@ -327,6 +403,170 @@ async def test_sending_hs_color(hass, mqtt_mock): state = hass.states.get('light.test') assert STATE_OFF == state.state + mqtt_mock.reset_mock() + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 210.824, "s": 100.0},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 359.0, "s": 78.0},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"h": 30.118, "s": 100.0},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + + +async def test_sending_rgb_color_no_brightness(hass, mqtt_mock): + """Test light.turn_on with hs color sends rgb color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'rgb': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + brightness=255) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 24, "b": 50}}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 50, "g": 11, "b": 11}}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0}}'), + 0, False), + ], any_order=True) + + +async def test_sending_rgb_color_with_brightness(hass, mqtt_mock): + """Test light.turn_on with hs color sends rgb color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'rgb': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 0, "g": 123, "b": 255},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 56, "b": 59},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"r": 255, "g": 128, "b": 0},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + + +async def test_sending_xy_color(hass, mqtt_mock): + """Test light.turn_on with hs color sends xy color parameters.""" + assert await async_setup_component(hass, light.DOMAIN, { + light.DOMAIN: { + 'platform': 'mqtt', + 'schema': 'json', + 'name': 'test', + 'command_topic': 'test_light_rgb/set', + 'brightness': True, + 'xy': True, + } + }) + + state = hass.states.get('light.test') + assert STATE_OFF == state.state + + common.async_turn_on(hass, 'light.test', + brightness=50, xy_color=[0.123, 0.123]) + common.async_turn_on(hass, 'light.test', + brightness=50, hs_color=[359, 78]) + common.async_turn_on(hass, 'light.test', rgb_color=[255, 128, 0], + white_value=80) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_has_calls([ + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.14, "y": 0.131},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.654, "y": 0.301},' + ' "brightness": 50}'), + 0, False), + mock.call( + 'test_light_rgb/set', + JsonValidator( + '{"state": "ON", "color": {"x": 0.611, "y": 0.375},' + ' "white_value": 80}'), + 0, False), + ], any_order=True) + async def test_flash_short_and_long(hass, mqtt_mock): """Test for flash length being sent when included.""" @@ -335,7 +575,6 @@ async def test_flash_short_and_long(hass, mqtt_mock): 'platform': 'mqtt', 'schema': 'json', 'name': 'test', - 'state_topic': 'test_light_rgb', 'command_topic': 'test_light_rgb/set', 'flash_time_short': 5, 'flash_time_long': 15, @@ -347,6 +586,26 @@ async def test_flash_short_and_long(hass, mqtt_mock): assert STATE_OFF == state.state assert 40 == state.attributes.get(ATTR_SUPPORTED_FEATURES) + common.async_turn_on(hass, 'light.test', flash='short') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "flash": 5}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_on(hass, 'light.test', flash='long') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "flash": 15}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + async def test_transition(hass, mqtt_mock): """Test for transition time being sent when included.""" @@ -355,7 +614,6 @@ async def test_transition(hass, mqtt_mock): 'platform': 'mqtt', 'schema': 'json', 'name': 'test', - 'state_topic': 'test_light_rgb', 'command_topic': 'test_light_rgb/set', 'qos': 0 } @@ -365,6 +623,26 @@ async def test_transition(hass, mqtt_mock): assert STATE_OFF == state.state assert 40 == state.attributes.get(ATTR_SUPPORTED_FEATURES) + common.async_turn_on(hass, 'light.test', transition=15) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "ON", "transition": 15}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_ON == state.state + + common.async_turn_off(hass, 'light.test', transition=30) + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'test_light_rgb/set', JsonValidator( + '{"state": "OFF", "transition": 30}'), 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('light.test') + assert STATE_OFF == state.state + async def test_brightness_scale(hass, mqtt_mock): """Test for brightness scaling.""" From fa8a4de019a3779aa53eff48a0140099c8a8581f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 10 Apr 2019 12:16:52 +0300 Subject: [PATCH 546/605] Upgrade pytest to 4.4.0 (#22822) --- requirements_test.txt | 2 +- requirements_test_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index 0cd8c583f38ec4..e8f916b0d06752 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -14,5 +14,5 @@ pytest-aiohttp==0.3.0 pytest-cov==2.6.1 pytest-sugar==0.9.2 pytest-timeout==1.3.3 -pytest==4.3.1 +pytest==4.4.0 requests_mock==1.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c1bb31edeb6f25..2767f3aa38e671 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -15,7 +15,7 @@ pytest-aiohttp==0.3.0 pytest-cov==2.6.1 pytest-sugar==0.9.2 pytest-timeout==1.3.3 -pytest==4.3.1 +pytest==4.4.0 requests_mock==1.5.2 From 7058249c01d7df7920e3285a5497c16703c8c473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 10 Apr 2019 12:17:02 +0300 Subject: [PATCH 547/605] Uprade asynctest to 0.12.3 (#22824) --- requirements_test.txt | 2 +- requirements_test_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index e8f916b0d06752..2fdb8992bfe75c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,7 +1,7 @@ # linters such as flake8 and pylint should be pinned, as new releases # make new things fail. Manually update these pins when pulling in a # new version -asynctest==0.12.2 +asynctest==0.12.3 codecov==2.0.15 coveralls==1.2.0 flake8-docstrings==1.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2767f3aa38e671..12c2f0e51ad0cf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2,7 +2,7 @@ # linters such as flake8 and pylint should be pinned, as new releases # make new things fail. Manually update these pins when pulling in a # new version -asynctest==0.12.2 +asynctest==0.12.3 codecov==2.0.15 coveralls==1.2.0 flake8-docstrings==1.3.0 From 6156bb4e5bb77b85e24558beabffc476edda24d3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 13:03:30 +0200 Subject: [PATCH 548/605] Upgrade Sphinx to 2.0.1 (#22960) --- requirements_docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_docs.txt b/requirements_docs.txt index eca1abdb365a36..ce1ea4c582143a 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1,3 +1,3 @@ -Sphinx==1.8.5 +Sphinx==2.0.1 sphinx-autodoc-typehints==1.6.0 sphinx-autodoc-annotation==1.0.post1 \ No newline at end of file From 3d5ee0eb58d9a58255b21cb5ce738c65ca163ee3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 13:05:16 +0200 Subject: [PATCH 549/605] Upgrade youtube_dl to 2019.04.07 (#22961) --- homeassistant/components/media_extractor/__init__.py | 2 +- homeassistant/components/media_extractor/manifest.json | 2 +- requirements_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_extractor/__init__.py b/homeassistant/components/media_extractor/__init__.py index 67dd06efab820a..f44075816affd2 100644 --- a/homeassistant/components/media_extractor/__init__.py +++ b/homeassistant/components/media_extractor/__init__.py @@ -12,7 +12,7 @@ ATTR_ENTITY_ID) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2019.03.18'] +REQUIREMENTS = ['youtube_dl==2019.04.07'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/media_extractor/manifest.json b/homeassistant/components/media_extractor/manifest.json index 53375f14bfedbd..431e711951a9a1 100644 --- a/homeassistant/components/media_extractor/manifest.json +++ b/homeassistant/components/media_extractor/manifest.json @@ -3,7 +3,7 @@ "name": "Media extractor", "documentation": "https://www.home-assistant.io/components/media_extractor", "requirements": [ - "youtube_dl==2019.03.18" + "youtube_dl==2019.04.07" ], "dependencies": [ "media_player" diff --git a/requirements_all.txt b/requirements_all.txt index b19bbc3951c90d..8e1e69fbb22b90 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1809,7 +1809,7 @@ yeelight==0.4.4 yeelightsunflower==0.0.10 # homeassistant.components.media_extractor -youtube_dl==2019.03.18 +youtube_dl==2019.04.07 # homeassistant.components.zengge zengge==0.2 From 691271147e426c6a775bf67dd282e55e8bbbb4f9 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 14:35:57 +0200 Subject: [PATCH 550/605] Update ordering (#22963) --- .../components/luci/device_tracker.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/luci/device_tracker.py b/homeassistant/components/luci/device_tracker.py index df65ed99f295d7..77273d89d42033 100644 --- a/homeassistant/components/luci/device_tracker.py +++ b/homeassistant/components/luci/device_tracker.py @@ -1,12 +1,13 @@ """Support for OpenWRT (luci) routers.""" import logging + import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.device_tracker import ( DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import ( - CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_SSL) + CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv REQUIREMENTS = ['openwrt-luci-rpc==1.0.5'] @@ -18,7 +19,7 @@ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean + vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, }) @@ -36,10 +37,9 @@ def __init__(self, config): """Initialize the scanner.""" from openwrt_luci_rpc import OpenWrtRpc - self.router = OpenWrtRpc(config[CONF_HOST], - config[CONF_USERNAME], - config[CONF_PASSWORD], - config[CONF_SSL]) + self.router = OpenWrtRpc( + config[CONF_HOST], config[CONF_USERNAME], config[CONF_PASSWORD], + config[CONF_SSL]) self.last_results = {} self.success_init = self.router.is_logged_in() @@ -62,9 +62,9 @@ def get_extra_attributes(self, device): Get extra attributes of a device. Some known extra attributes that may be returned in the device tuple - include Mac Address (mac), Network Device (dev), Ip Address - (ip), reachable status (reachable), Associated router - (host), Hostname if known (hostname) among others. + include MAC address (mac), network device (dev), IP address + (ip), reachable status (reachable), associated router + (host), hostname if known (hostname) among others. """ device = next(( result for result in self.last_results @@ -76,8 +76,7 @@ def _update_info(self): result = self.router.get_all_connected_devices( only_reachable=True) - _LOGGER.debug("Luci get_all_connected_devices returned:" - " %s", result) + _LOGGER.debug("Luci get_all_connected_devices returned: %s", result) last_results = [] for device in result: From f4309dfcc60176523bf2bcd24c25a2c504dacfda Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 14:51:42 +0200 Subject: [PATCH 551/605] Add missing attribution (#22964) --- .../components/london_underground/sensor.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/london_underground/sensor.py b/homeassistant/components/london_underground/sensor.py index 948b60b1b79d61..c2502e2ab2be60 100644 --- a/homeassistant/components/london_underground/sensor.py +++ b/homeassistant/components/london_underground/sensor.py @@ -1,11 +1,12 @@ """Sensor for checking the status of London Underground tube lines.""" -import logging from datetime import timedelta +import logging import voluptuous as vol -import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ATTR_ATTRIBUTION +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity REQUIREMENTS = ['london-tube-status==0.2'] @@ -16,6 +17,8 @@ CONF_LINE = 'line' +ICON = 'mdi:subway' + SCAN_INTERVAL = timedelta(seconds=30) TUBE_LINES = [ @@ -54,16 +57,15 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class LondonTubeSensor(Entity): - """Sensor that reads the status of a line from TubeData.""" - - ICON = 'mdi:subway' + """Sensor that reads the status of a line from Tube Data.""" def __init__(self, name, data): - """Initialize the sensor.""" - self._name = name + """Initialize the London Underground sensor.""" self._data = data - self._state = None self._description = None + self._name = name + self._state = None + self.attrs = {ATTR_ATTRIBUTION: ATTRIBUTION} @property def name(self): @@ -78,14 +80,13 @@ def state(self): @property def icon(self): """Icon to use in the frontend, if any.""" - return self.ICON + return ICON @property def device_state_attributes(self): """Return other details about the sensor state.""" - attrs = {} - attrs['Description'] = self._description - return attrs + self.attrs['Description'] = self._description + return self.attrs def update(self): """Update the sensor.""" From be51a3ae12454062c35c6273db267f7932d9ca89 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 15:15:12 +0200 Subject: [PATCH 552/605] Upgrade ruamel.yaml to 0.15.91 (#22965) --- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 5b0673038bb1bc..f68f16d9e9bd63 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ python-slugify==1.2.6 pytz>=2018.07 pyyaml>=3.13,<4 requests==2.21.0 -ruamel.yaml==0.15.89 +ruamel.yaml==0.15.91 voluptuous==0.11.5 voluptuous-serialize==2.1.0 diff --git a/requirements_all.txt b/requirements_all.txt index 8e1e69fbb22b90..77abbdab341d77 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -13,7 +13,7 @@ python-slugify==1.2.6 pytz>=2018.07 pyyaml>=3.13,<4 requests==2.21.0 -ruamel.yaml==0.15.89 +ruamel.yaml==0.15.91 voluptuous==0.11.5 voluptuous-serialize==2.1.0 diff --git a/setup.py b/setup.py index 0245923afb1615..2f2c310e572c56 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ 'pytz>=2018.07', 'pyyaml>=3.13,<4', 'requests==2.21.0', - 'ruamel.yaml==0.15.89', + 'ruamel.yaml==0.15.91', 'voluptuous==0.11.5', 'voluptuous-serialize==2.1.0', ] From 9e56283eaf998f59e4e07dd61999c4c5ac8d919c Mon Sep 17 00:00:00 2001 From: John Raahauge <43510812+AZDane@users.noreply.github.com> Date: Mon, 8 Apr 2019 05:53:00 -0700 Subject: [PATCH 553/605] Fix position of add_entities of binary sensor (#22866) * Bugfix - binary_sensor.py * Added features to Concord232 Alarm Panel * Added New Line End Of File * Deleted Whitespace * Back to original Removed added feature and sticking to bugfix --- homeassistant/components/concord232/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/concord232/binary_sensor.py b/homeassistant/components/concord232/binary_sensor.py index 26f35d60305825..3e4d0526db4044 100644 --- a/homeassistant/components/concord232/binary_sensor.py +++ b/homeassistant/components/concord232/binary_sensor.py @@ -79,7 +79,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): ) ) - add_entities(sensors, True) + add_entities(sensors, True) def get_opening_type(zone): From f531ca61c696e16f14a7df103268b9179d90cb4e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 10 Apr 2019 15:18:30 +0200 Subject: [PATCH 554/605] Set pytz>=2019.01 (#22966) --- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f68f16d9e9bd63..68c4a627083ed3 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -9,7 +9,7 @@ PyJWT==1.7.1 cryptography==2.6.1 pip>=8.0.3 python-slugify==1.2.6 -pytz>=2018.07 +pytz>=2019.01 pyyaml>=3.13,<4 requests==2.21.0 ruamel.yaml==0.15.91 diff --git a/requirements_all.txt b/requirements_all.txt index 77abbdab341d77..dd5aec398c7bcc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -10,7 +10,7 @@ PyJWT==1.7.1 cryptography==2.6.1 pip>=8.0.3 python-slugify==1.2.6 -pytz>=2018.07 +pytz>=2019.01 pyyaml>=3.13,<4 requests==2.21.0 ruamel.yaml==0.15.91 diff --git a/setup.py b/setup.py index 2f2c310e572c56..9be6bbd3a310ed 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ 'cryptography==2.6.1', 'pip>=8.0.3', 'python-slugify==1.2.6', - 'pytz>=2018.07', + 'pytz>=2019.01', 'pyyaml>=3.13,<4', 'requests==2.21.0', 'ruamel.yaml==0.15.91', From a267df2abb97428dce40ea101349f438ae7c1178 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 9 Apr 2019 02:48:59 -0700 Subject: [PATCH 555/605] More Mobile app sensor fixes (#22914) * Ensure we only add a sensor once * Ensure that we dont process updates for entities that arent what we were setup for * Add debug logging to ease development of apps * Use str representation --- .../components/mobile_app/binary_sensor.py | 13 ++++++++++++- homeassistant/components/mobile_app/entity.py | 14 ++++++++++++-- homeassistant/components/mobile_app/sensor.py | 15 +++++++++++++-- homeassistant/components/mobile_app/webhook.py | 3 +++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 289a50584c92d3..50943bb6504d8b 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -8,9 +8,10 @@ from .const import (ATTR_SENSOR_STATE, ATTR_SENSOR_TYPE_BINARY_SENSOR as ENTITY_TYPE, + ATTR_SENSOR_UNIQUE_ID, DATA_DEVICES, DOMAIN) -from .entity import MobileAppEntity +from .entity import MobileAppEntity, sensor_id DEPENDENCIES = ['mobile_app'] @@ -36,6 +37,16 @@ def handle_sensor_registration(webhook_id, data): if data[CONF_WEBHOOK_ID] != webhook_id: return + unique_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + + entity = hass.data[DOMAIN][ENTITY_TYPE][unique_id] + + if 'added' in entity: + return + + entity['added'] = True + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] async_add_entities([MobileAppBinarySensor(data, device, config_entry)]) diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index 05736b3a689b37..eca9d2b024bd40 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -13,6 +13,11 @@ DOMAIN, SIGNAL_SENSOR_UPDATE) +def sensor_id(webhook_id, unique_id): + """Return a unique sensor ID.""" + return "{}_{}".format(webhook_id, unique_id) + + class MobileAppEntity(Entity): """Representation of an mobile app entity.""" @@ -22,8 +27,8 @@ def __init__(self, config: dict, device: DeviceEntry, entry: ConfigEntry): self._device = device self._entry = entry self._registration = entry.data - self._sensor_id = "{}_{}".format(self._registration[CONF_WEBHOOK_ID], - config[ATTR_SENSOR_UNIQUE_ID]) + self._sensor_id = sensor_id(self._registration[CONF_WEBHOOK_ID], + config[ATTR_SENSOR_UNIQUE_ID]) self._entity_type = config[ATTR_SENSOR_TYPE] self.unsub_dispatcher = None @@ -94,5 +99,10 @@ async def async_update(self): @callback def _handle_update(self, data): """Handle async event updates.""" + incoming_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + if incoming_id != self._sensor_id: + return + self._config = data self.async_schedule_update_ha_state() diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index b2846a6002b17f..64ad69c5758ecc 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -7,9 +7,10 @@ from .const import (ATTR_SENSOR_STATE, ATTR_SENSOR_TYPE_SENSOR as ENTITY_TYPE, - ATTR_SENSOR_UOM, DATA_DEVICES, DOMAIN) + ATTR_SENSOR_UNIQUE_ID, ATTR_SENSOR_UOM, DATA_DEVICES, + DOMAIN) -from .entity import MobileAppEntity +from .entity import MobileAppEntity, sensor_id DEPENDENCIES = ['mobile_app'] @@ -35,6 +36,16 @@ def handle_sensor_registration(webhook_id, data): if data[CONF_WEBHOOK_ID] != webhook_id: return + unique_id = sensor_id(data[CONF_WEBHOOK_ID], + data[ATTR_SENSOR_UNIQUE_ID]) + + entity = hass.data[DOMAIN][ENTITY_TYPE][unique_id] + + if 'added' in entity: + return + + entity['added'] = True + device = hass.data[DOMAIN][DATA_DEVICES][data[CONF_WEBHOOK_ID]] async_add_entities([MobileAppSensor(data, device, config_entry)]) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 28ef6bccd6add2..7a868c6ac6af47 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -96,6 +96,9 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, data = webhook_payload + _LOGGER.debug("Received webhook payload for type %s: %s", webhook_type, + data) + if webhook_type in WEBHOOK_SCHEMAS: try: data = WEBHOOK_SCHEMAS[webhook_type](webhook_payload) From e7a17b710d52845cc92f95cca29d0b47860642e5 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 9 Apr 2019 02:47:57 -0700 Subject: [PATCH 556/605] Add cloudhook and remote UI vals to get_config (#22921) --- .../components/mobile_app/webhook.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 7a868c6ac6af47..1ef5f4ce531764 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -4,6 +4,8 @@ from aiohttp.web import HTTPBadRequest, Response, Request import voluptuous as vol +from homeassistant.components.cloud import (async_remote_ui_url, + CloudNotAvailable) from homeassistant.components.device_tracker import (ATTR_ATTRIBUTES, ATTR_DEV_ID, DOMAIN as DT_DOMAIN, @@ -31,14 +33,15 @@ ATTR_TEMPLATE_VARIABLES, ATTR_VERTICAL_ACCURACY, ATTR_WEBHOOK_DATA, ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_TYPE, - CONF_SECRET, DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, - DATA_STORE, DOMAIN, ERR_ENCRYPTION_REQUIRED, - ERR_SENSOR_DUPLICATE_UNIQUE_ID, ERR_SENSOR_NOT_REGISTERED, - SIGNAL_SENSOR_UPDATE, WEBHOOK_PAYLOAD_SCHEMA, - WEBHOOK_SCHEMAS, WEBHOOK_TYPES, WEBHOOK_TYPE_CALL_SERVICE, - WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_GET_CONFIG, - WEBHOOK_TYPE_GET_ZONES, WEBHOOK_TYPE_REGISTER_SENSOR, - WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION, + CONF_CLOUDHOOK_URL, CONF_REMOTE_UI_URL, CONF_SECRET, + DATA_CONFIG_ENTRIES, DATA_DELETED_IDS, DATA_STORE, DOMAIN, + ERR_ENCRYPTION_REQUIRED, ERR_SENSOR_DUPLICATE_UNIQUE_ID, + ERR_SENSOR_NOT_REGISTERED, SIGNAL_SENSOR_UPDATE, + WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS, WEBHOOK_TYPES, + WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT, + WEBHOOK_TYPE_GET_CONFIG, WEBHOOK_TYPE_GET_ZONES, + WEBHOOK_TYPE_REGISTER_SENSOR, WEBHOOK_TYPE_RENDER_TEMPLATE, + WEBHOOK_TYPE_UPDATE_LOCATION, WEBHOOK_TYPE_UPDATE_REGISTRATION, WEBHOOK_TYPE_UPDATE_SENSOR_STATES) @@ -289,7 +292,7 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, hass_config = hass.config.as_dict() - return webhook_response({ + resp = { 'latitude': hass_config['latitude'], 'longitude': hass_config['longitude'], 'elevation': hass_config['elevation'], @@ -299,4 +302,15 @@ async def handle_webhook(hass: HomeAssistantType, webhook_id: str, 'components': hass_config['components'], 'version': hass_config['version'], 'theme_color': MANIFEST_JSON['theme_color'], - }, registration=registration, headers=headers) + } + + if CONF_CLOUDHOOK_URL in registration: + resp[CONF_CLOUDHOOK_URL] = registration[CONF_CLOUDHOOK_URL] + + try: + resp[CONF_REMOTE_UI_URL] = async_remote_ui_url(hass) + except CloudNotAvailable: + pass + + return webhook_response(resp, registration=registration, + headers=headers) From eab575e65db29fd3a6f633ec4bf88b31e6b29795 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 10 Apr 2019 05:13:39 +0200 Subject: [PATCH 557/605] Bugfix: pass protocol out of header to application layer (#22955) --- homeassistant/components/hassio/ingress.py | 41 +++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index 4f7c99618c19d4..f4cc97c385392e 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -70,7 +70,18 @@ async def _handle_websocket( self, request: web.Request, token: str, path: str ) -> web.WebSocketResponse: """Ingress route for websocket.""" - ws_server = web.WebSocketResponse() + if hdrs.SEC_WEBSOCKET_PROTOCOL in request.headers: + req_protocols = [ + str(proto.strip()) + for proto in + request.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + else: + req_protocols = () + + ws_server = web.WebSocketResponse( + protocols=req_protocols, autoclose=False, autoping=False + ) await ws_server.prepare(request) # Preparing @@ -83,7 +94,8 @@ async def _handle_websocket( # Start proxy async with self._websession.ws_connect( - url, headers=source_header + url, headers=source_header, protocols=req_protocols, + autoclose=False, autoping=False, ) as ws_client: # Proxy requests await asyncio.wait( @@ -205,14 +217,17 @@ def _is_websocket(request: web.Request) -> bool: async def _websocket_forward(ws_from, ws_to): """Handle websocket message directly.""" - async for msg in ws_from: - if msg.type == aiohttp.WSMsgType.TEXT: - await ws_to.send_str(msg.data) - elif msg.type == aiohttp.WSMsgType.BINARY: - await ws_to.send_bytes(msg.data) - elif msg.type == aiohttp.WSMsgType.PING: - await ws_to.ping() - elif msg.type == aiohttp.WSMsgType.PONG: - await ws_to.pong() - elif ws_to.closed: - await ws_to.close(code=ws_to.close_code, message=msg.extra) + try: + async for msg in ws_from: + if msg.type == aiohttp.WSMsgType.TEXT: + await ws_to.send_str(msg.data) + elif msg.type == aiohttp.WSMsgType.BINARY: + await ws_to.send_bytes(msg.data) + elif msg.type == aiohttp.WSMsgType.PING: + await ws_to.ping() + elif msg.type == aiohttp.WSMsgType.PONG: + await ws_to.pong() + elif ws_to.closed: + await ws_to.close(code=ws_to.close_code, message=msg.extra) + except RuntimeError: + _LOGGER.debug("Ingress Websocket runtime error") From 984af45bb299faf0bf613547734c3e1b9e70e629 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 10 Apr 2019 13:22:19 +0000 Subject: [PATCH 558/605] Bumped version to 0.91.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 28bdf2f7fc3807..6a8a7fe6a1d01d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 91 -PATCH_VERSION = '2' +PATCH_VERSION = '3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From fc7a187dd6472b2b0e4dab27b5fe7b37f512aaf2 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 10 Apr 2019 16:46:23 +0200 Subject: [PATCH 559/605] Update uvloop 0.12.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index aa9415fd1e0801..98a45abf0ea16b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ COPY requirements_all.txt requirements_all.txt # Uninstall enum34 because some dependencies install it but breaks Python 3.4+. # See PR #8103 for more info. RUN pip3 install --no-cache-dir -r requirements_all.txt && \ - pip3 install --no-cache-dir mysqlclient psycopg2 uvloop==0.11.3 cchardet cython tensorflow + pip3 install --no-cache-dir mysqlclient psycopg2 uvloop==0.12.2 cchardet cython tensorflow # Copy source COPY . . From 7624d0e79f3413e2f54851499740c7470ffa139d Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Wed, 10 Apr 2019 11:44:58 -0500 Subject: [PATCH 560/605] Check for supported features in media_player services (#22878) * Add check for supported features * Move logic to service helper * Fix hacked in test for seek * Test for service required features --- homeassistant/components/demo/media_player.py | 14 +-- .../components/media_player/__init__.py | 100 +++++++----------- homeassistant/helpers/entity_component.py | 6 +- homeassistant/helpers/service.py | 14 ++- tests/components/demo/test_media_player.py | 14 ++- tests/helpers/test_service.py | 15 +++ 6 files changed, 84 insertions(+), 79 deletions(-) diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py index 5ad13b4e9953ae..cb3f3b5b46a60f 100644 --- a/homeassistant/components/demo/media_player.py +++ b/homeassistant/components/demo/media_player.py @@ -1,14 +1,13 @@ """Demo implementation of the media player.""" -from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING -import homeassistant.util.dt as dt_util - from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, - SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOUND_MODE, - SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_TURN_OFF, - SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) + SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, + SUPPORT_SELECT_SOUND_MODE, SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, + SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) +from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING +import homeassistant.util.dt as dt_util def setup_platform(hass, config, add_entities, discovery_info=None): @@ -30,7 +29,8 @@ def setup_platform(hass, config, add_entities, discovery_info=None): YOUTUBE_PLAYER_SUPPORT = \ SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | SUPPORT_PLAY | \ - SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOUND_MODE | SUPPORT_SELECT_SOURCE + SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOUND_MODE | SUPPORT_SELECT_SOURCE | \ + SUPPORT_SEEK MUSIC_PLAYER_SUPPORT = \ SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 3421551320e203..5bc2d640e2bd56 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -32,51 +32,20 @@ from homeassistant.loader import bind_hass from .const import ( - ATTR_APP_ID, - ATTR_APP_NAME, - ATTR_INPUT_SOURCE, - ATTR_INPUT_SOURCE_LIST, - ATTR_MEDIA_ALBUM_ARTIST, - ATTR_MEDIA_ALBUM_NAME, - ATTR_MEDIA_ARTIST, - ATTR_MEDIA_CHANNEL, - ATTR_MEDIA_CONTENT_ID, - ATTR_MEDIA_CONTENT_TYPE, - ATTR_MEDIA_DURATION, - ATTR_MEDIA_ENQUEUE, - ATTR_MEDIA_EPISODE, - ATTR_MEDIA_PLAYLIST, - ATTR_MEDIA_POSITION, - ATTR_MEDIA_POSITION_UPDATED_AT, - ATTR_MEDIA_SEASON, - ATTR_MEDIA_SEEK_POSITION, - ATTR_MEDIA_SERIES_TITLE, - ATTR_MEDIA_SHUFFLE, - ATTR_MEDIA_TITLE, - ATTR_MEDIA_TRACK, - ATTR_MEDIA_VOLUME_LEVEL, - ATTR_MEDIA_VOLUME_MUTED, - ATTR_SOUND_MODE, - ATTR_SOUND_MODE_LIST, - DOMAIN, - SERVICE_CLEAR_PLAYLIST, - SERVICE_PLAY_MEDIA, - SERVICE_SELECT_SOUND_MODE, - SERVICE_SELECT_SOURCE, - SUPPORT_PAUSE, - SUPPORT_SEEK, - SUPPORT_VOLUME_SET, - SUPPORT_VOLUME_MUTE, - SUPPORT_PREVIOUS_TRACK, - SUPPORT_NEXT_TRACK, - SUPPORT_PLAY_MEDIA, - SUPPORT_SELECT_SOURCE, - SUPPORT_STOP, - SUPPORT_CLEAR_PLAYLIST, - SUPPORT_PLAY, - SUPPORT_SHUFFLE_SET, - SUPPORT_SELECT_SOUND_MODE, -) + ATTR_APP_ID, ATTR_APP_NAME, ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, + ATTR_MEDIA_ALBUM_ARTIST, ATTR_MEDIA_ALBUM_NAME, ATTR_MEDIA_ARTIST, + ATTR_MEDIA_CHANNEL, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, + ATTR_MEDIA_DURATION, ATTR_MEDIA_ENQUEUE, ATTR_MEDIA_EPISODE, + ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_POSITION, ATTR_MEDIA_POSITION_UPDATED_AT, + ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION, ATTR_MEDIA_SERIES_TITLE, + ATTR_MEDIA_SHUFFLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK, + ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_SOUND_MODE, + ATTR_SOUND_MODE_LIST, DOMAIN, SERVICE_CLEAR_PLAYLIST, SERVICE_PLAY_MEDIA, + SERVICE_SELECT_SOUND_MODE, SERVICE_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST, + SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, + SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_SELECT_SOUND_MODE, + SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_STOP, SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET) from .reproduce_state import async_reproduce_states # noqa _LOGGER = logging.getLogger(__name__) @@ -197,74 +166,77 @@ async def async_setup(hass, config): component.async_register_entity_service( SERVICE_TURN_ON, MEDIA_PLAYER_SCHEMA, - 'async_turn_on' + 'async_turn_on', SUPPORT_TURN_ON ) component.async_register_entity_service( SERVICE_TURN_OFF, MEDIA_PLAYER_SCHEMA, - 'async_turn_off' + 'async_turn_off', SUPPORT_TURN_OFF ) component.async_register_entity_service( SERVICE_TOGGLE, MEDIA_PLAYER_SCHEMA, - 'async_toggle' + 'async_toggle', SUPPORT_TURN_OFF | SUPPORT_TURN_ON ) component.async_register_entity_service( SERVICE_VOLUME_UP, MEDIA_PLAYER_SCHEMA, - 'async_volume_up' + 'async_volume_up', SUPPORT_VOLUME_SET ) component.async_register_entity_service( SERVICE_VOLUME_DOWN, MEDIA_PLAYER_SCHEMA, - 'async_volume_down' + 'async_volume_down', SUPPORT_VOLUME_SET ) component.async_register_entity_service( SERVICE_MEDIA_PLAY_PAUSE, MEDIA_PLAYER_SCHEMA, - 'async_media_play_pause' + 'async_media_play_pause', SUPPORT_PLAY | SUPPORT_PAUSE ) component.async_register_entity_service( SERVICE_MEDIA_PLAY, MEDIA_PLAYER_SCHEMA, - 'async_media_play' + 'async_media_play', SUPPORT_PLAY ) component.async_register_entity_service( SERVICE_MEDIA_PAUSE, MEDIA_PLAYER_SCHEMA, - 'async_media_pause' + 'async_media_pause', SUPPORT_PAUSE ) component.async_register_entity_service( SERVICE_MEDIA_STOP, MEDIA_PLAYER_SCHEMA, - 'async_media_stop' + 'async_media_stop', SUPPORT_STOP ) component.async_register_entity_service( SERVICE_MEDIA_NEXT_TRACK, MEDIA_PLAYER_SCHEMA, - 'async_media_next_track' + 'async_media_next_track', SUPPORT_NEXT_TRACK ) component.async_register_entity_service( SERVICE_MEDIA_PREVIOUS_TRACK, MEDIA_PLAYER_SCHEMA, - 'async_media_previous_track' + 'async_media_previous_track', SUPPORT_PREVIOUS_TRACK ) component.async_register_entity_service( SERVICE_CLEAR_PLAYLIST, MEDIA_PLAYER_SCHEMA, - 'async_clear_playlist' + 'async_clear_playlist', SUPPORT_CLEAR_PLAYLIST ) component.async_register_entity_service( SERVICE_VOLUME_SET, MEDIA_PLAYER_SET_VOLUME_SCHEMA, lambda entity, call: entity.async_set_volume_level( - volume=call.data[ATTR_MEDIA_VOLUME_LEVEL]) + volume=call.data[ATTR_MEDIA_VOLUME_LEVEL]), + SUPPORT_VOLUME_SET ) component.async_register_entity_service( SERVICE_VOLUME_MUTE, MEDIA_PLAYER_MUTE_VOLUME_SCHEMA, lambda entity, call: entity.async_mute_volume( - mute=call.data[ATTR_MEDIA_VOLUME_MUTED]) + mute=call.data[ATTR_MEDIA_VOLUME_MUTED]), + SUPPORT_VOLUME_MUTE ) component.async_register_entity_service( SERVICE_MEDIA_SEEK, MEDIA_PLAYER_MEDIA_SEEK_SCHEMA, lambda entity, call: entity.async_media_seek( - position=call.data[ATTR_MEDIA_SEEK_POSITION]) + position=call.data[ATTR_MEDIA_SEEK_POSITION]), + SUPPORT_SEEK ) component.async_register_entity_service( SERVICE_SELECT_SOURCE, MEDIA_PLAYER_SELECT_SOURCE_SCHEMA, - 'async_select_source' + 'async_select_source', SUPPORT_SELECT_SOURCE ) component.async_register_entity_service( SERVICE_SELECT_SOUND_MODE, MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA, - 'async_select_sound_mode' + 'async_select_sound_mode', SUPPORT_SELECT_SOUND_MODE ) component.async_register_entity_service( SERVICE_PLAY_MEDIA, MEDIA_PLAYER_PLAY_MEDIA_SCHEMA, @@ -272,11 +244,11 @@ async def async_setup(hass, config): media_type=call.data[ATTR_MEDIA_CONTENT_TYPE], media_id=call.data[ATTR_MEDIA_CONTENT_ID], enqueue=call.data.get(ATTR_MEDIA_ENQUEUE) - ) + ), SUPPORT_PLAY_MEDIA ) component.async_register_entity_service( SERVICE_SHUFFLE_SET, MEDIA_PLAYER_SET_SHUFFLE_SCHEMA, - 'async_set_shuffle' + 'async_set_shuffle', SUPPORT_SHUFFLE_SET ) return True diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index 744cf36ea66c1d..7be3d906bfa83e 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -179,13 +179,15 @@ async def async_extract_from_service(self, service, expand_group=True): if entity.available and entity.entity_id in entity_ids] @callback - def async_register_entity_service(self, name, schema, func): + def async_register_entity_service(self, name, schema, func, + required_features=None): """Register an entity service.""" async def handle_service(call): """Handle the service.""" service_name = "{}.{}".format(self.domain, name) await self.hass.helpers.service.entity_service_call( - self._platforms.values(), func, call, service_name + self._platforms.values(), func, call, service_name, + required_features ) self.hass.services.async_register( diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 3892dbb660733c..ea62d12c66c02e 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -216,7 +216,8 @@ def load_services_files(yaml_files): @bind_hass -async def entity_service_call(hass, platforms, func, call, service_name=''): +async def entity_service_call(hass, platforms, func, call, service_name='', + required_features=None): """Handle an entity service call. Calls all platforms simultaneously. @@ -295,7 +296,8 @@ async def entity_service_call(hass, platforms, func, call, service_name=''): platforms_entities.append(platform_entities) tasks = [ - _handle_service_platform_call(func, data, entities, call.context) + _handle_service_platform_call(func, data, entities, call.context, + required_features) for platform, entities in zip(platforms, platforms_entities) ] @@ -306,7 +308,8 @@ async def entity_service_call(hass, platforms, func, call, service_name=''): future.result() # pop exception if have -async def _handle_service_platform_call(func, data, entities, context): +async def _handle_service_platform_call(func, data, entities, context, + required_features): """Handle a function call.""" tasks = [] @@ -314,6 +317,11 @@ async def _handle_service_platform_call(func, data, entities, context): if not entity.available: continue + # Skip entities that don't have the required feature. + if required_features is not None \ + and not entity.supported_features & required_features: + continue + entity.async_set_context(context) if isinstance(func, str): diff --git a/tests/components/demo/test_media_player.py b/tests/components/demo/test_media_player.py index 83acf8be601bd5..808e3ee2102a4b 100644 --- a/tests/components/demo/test_media_player.py +++ b/tests/components/demo/test_media_player.py @@ -184,9 +184,7 @@ def test_prev_next_track(self): state = self.hass.states.get(ent_id) assert 1 == state.attributes.get('media_episode') - @patch('homeassistant.components.demo.media_player.DemoYoutubePlayer.' - 'media_seek', autospec=True) - def test_play_media(self, mock_seek): + def test_play_media(self): """Test play_media .""" assert setup_component( self.hass, mp.DOMAIN, @@ -212,6 +210,16 @@ def test_play_media(self, mock_seek): state.attributes.get('supported_features')) assert 'some_id' == state.attributes.get('media_content_id') + @patch('homeassistant.components.demo.media_player.DemoYoutubePlayer.' + 'media_seek', autospec=True) + def test_seek(self, mock_seek): + """Test seek.""" + assert setup_component( + self.hass, mp.DOMAIN, + {'media_player': {'platform': 'demo'}}) + ent_id = 'media_player.living_room' + state = self.hass.states.get(ent_id) + assert state.attributes['supported_features'] & mp.SUPPORT_SEEK assert not mock_seek.called with pytest.raises(vol.Invalid): common.media_seek(self.hass, None, ent_id) diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index f59a01ec268469..231ffddff3095c 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -37,11 +37,13 @@ def mock_entities(): entity_id='light.kitchen', available=True, should_poll=False, + supported_features=1, ) living_room = Mock( entity_id='light.living_room', available=True, should_poll=False, + supported_features=0, ) entities = OrderedDict() entities[kitchen.entity_id] = kitchen @@ -269,6 +271,19 @@ def test_async_get_all_descriptions(hass): assert 'fields' in descriptions[logger.DOMAIN]['set_level'] +async def test_call_with_required_features(hass, mock_entities): + """Test service calls invoked only if entity has required feautres.""" + test_service_mock = Mock(return_value=mock_coro()) + await service.entity_service_call(hass, [ + Mock(entities=mock_entities) + ], test_service_mock, ha.ServiceCall('test_domain', 'test_service', { + 'entity_id': 'all' + }), required_features=1) + assert len(mock_entities) == 2 + # Called once because only one of the entities had the required features + assert test_service_mock.call_count == 1 + + async def test_call_context_user_not_exist(hass): """Check we don't allow deleted users to do things.""" with pytest.raises(exceptions.UnknownUser) as err: From 72af4276b97a31ef206d495ef5765d917374a2ef Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 10 Apr 2019 22:13:13 +0200 Subject: [PATCH 561/605] Add ESPHome climate support (#22859) * Add ESPHome climate support * Adjust line length * Update .coveragerc * Update climate.py * Rename --- .coveragerc | 2 + homeassistant/components/esphome/__init__.py | 1 + homeassistant/components/esphome/climate.py | 203 +++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 homeassistant/components/esphome/climate.py diff --git a/.coveragerc b/.coveragerc index eaf00c7e6ece2a..ba6f27d3655a5a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -167,6 +167,8 @@ omit = homeassistant/components/eq3btsmart/climate.py homeassistant/components/esphome/__init__.py homeassistant/components/esphome/binary_sensor.py + homeassistant/components/esphome/camera.py + homeassistant/components/esphome/climate.py homeassistant/components/esphome/cover.py homeassistant/components/esphome/fan.py homeassistant/components/esphome/light.py diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 19cd851002a284..9e6f6367cda4fd 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -49,6 +49,7 @@ HA_COMPONENTS = [ 'binary_sensor', 'camera', + 'climate', 'cover', 'fan', 'light', diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py new file mode 100644 index 00000000000000..e3cd9e488bf60d --- /dev/null +++ b/homeassistant/components/esphome/climate.py @@ -0,0 +1,203 @@ +"""Support for ESPHome climate devices.""" +import logging +import math +from typing import TYPE_CHECKING, List, Optional + +from homeassistant.components.climate import ClimateDevice +from homeassistant.components.climate.const import ( + ATTR_OPERATION_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, + STATE_AUTO, STATE_COOL, STATE_HEAT, SUPPORT_AWAY_MODE, + SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE, + SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) +from homeassistant.const import ( + ATTR_TEMPERATURE, PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, + STATE_OFF, TEMP_CELSIUS) + +from . import EsphomeEntity, platform_async_setup_entry + +if TYPE_CHECKING: + # pylint: disable=unused-import + from aioesphomeapi import ClimateInfo, ClimateState, ClimateMode # noqa + +DEPENDENCIES = ['esphome'] +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up ESPHome climate devices based on a config entry.""" + # pylint: disable=redefined-outer-name + from aioesphomeapi import ClimateInfo, ClimateState # noqa + + await platform_async_setup_entry( + hass, entry, async_add_entities, + component_key='climate', + info_type=ClimateInfo, entity_type=EsphomeClimateDevice, + state_type=ClimateState + ) + + +def _ha_climate_mode_to_esphome(mode: str) -> 'ClimateMode': + # pylint: disable=redefined-outer-name + from aioesphomeapi import ClimateMode # noqa + return { + STATE_OFF: ClimateMode.OFF, + STATE_AUTO: ClimateMode.AUTO, + STATE_COOL: ClimateMode.COOL, + STATE_HEAT: ClimateMode.HEAT, + }[mode] + + +def _esphome_climate_mode_to_ha(mode: 'ClimateMode') -> str: + # pylint: disable=redefined-outer-name + from aioesphomeapi import ClimateMode # noqa + return { + ClimateMode.OFF: STATE_OFF, + ClimateMode.AUTO: STATE_AUTO, + ClimateMode.COOL: STATE_COOL, + ClimateMode.HEAT: STATE_HEAT, + }[mode] + + +class EsphomeClimateDevice(EsphomeEntity, ClimateDevice): + """A climate implementation for ESPHome.""" + + @property + def _static_info(self) -> 'ClimateInfo': + return super()._static_info + + @property + def _state(self) -> Optional['ClimateState']: + return super()._state + + @property + def precision(self) -> float: + """Return the precision of the climate device.""" + precicions = [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] + for prec in precicions: + if self._static_info.visual_temperature_step >= prec: + return prec + # Fall back to highest precision, tenths + return PRECISION_TENTHS + + @property + def temperature_unit(self) -> str: + """Return the unit of measurement used by the platform.""" + return TEMP_CELSIUS + + @property + def operation_list(self) -> List[str]: + """Return the list of available operation modes.""" + return [ + _esphome_climate_mode_to_ha(mode) + for mode in self._static_info.supported_modes + ] + + @property + def target_temperature_step(self): + """Return the supported step of target temperature.""" + # Round to one digit because of floating point math + return round(self._static_info.visual_temperature_step, 1) + + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + return self._static_info.visual_min_temperature + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + return self._static_info.visual_max_temperature + + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + features = SUPPORT_OPERATION_MODE + if self._static_info.supports_two_point_target_temperature: + features |= (SUPPORT_TARGET_TEMPERATURE_LOW | + SUPPORT_TARGET_TEMPERATURE_HIGH) + else: + features |= SUPPORT_TARGET_TEMPERATURE + if self._static_info.supports_away: + features |= SUPPORT_AWAY_MODE + return features + + @property + def current_operation(self) -> Optional[str]: + """Return current operation ie. heat, cool, idle.""" + if self._state is None: + return None + return _esphome_climate_mode_to_ha(self._state.mode) + + @property + def current_temperature(self) -> Optional[float]: + """Return the current temperature.""" + if self._state is None: + return None + if math.isnan(self._state.current_temperature): + return None + return self._state.current_temperature + + @property + def target_temperature(self) -> Optional[float]: + """Return the temperature we try to reach.""" + if self._state is None: + return None + if math.isnan(self._state.target_temperature): + return None + return self._state.target_temperature + + @property + def target_temperature_low(self): + """Return the lowbound target temperature we try to reach.""" + if self._state is None: + return None + if math.isnan(self._state.target_temperature_low): + return None + return self._state.target_temperature_low + + @property + def target_temperature_high(self): + """Return the highbound target temperature we try to reach.""" + if self._state is None: + return None + if math.isnan(self._state.target_temperature_high): + return None + return self._state.target_temperature_high + + @property + def is_away_mode_on(self): + """Return true if away mode is on.""" + if self._state is None: + return None + return self._state.away + + async def async_set_temperature(self, **kwargs): + """Set new target temperature (and operation mode if set).""" + data = {'key': self._static_info.key} + if ATTR_OPERATION_MODE in kwargs: + data['mode'] = _ha_climate_mode_to_esphome( + kwargs[ATTR_OPERATION_MODE]) + if ATTR_TEMPERATURE in kwargs: + data['target_temperature'] = kwargs[ATTR_TEMPERATURE] + if ATTR_TARGET_TEMP_LOW in kwargs: + data['target_temperature_low'] = kwargs[ATTR_TARGET_TEMP_LOW] + if ATTR_TARGET_TEMP_HIGH in kwargs: + data['target_temperature_high'] = kwargs[ATTR_TARGET_TEMP_HIGH] + await self._client.climate_command(**data) + + async def async_set_operation_mode(self, operation_mode): + """Set new target operation mode.""" + await self._client.climate_command( + key=self._static_info.key, + mode=_ha_climate_mode_to_esphome(operation_mode), + ) + + async def async_turn_away_mode_on(self): + """Turn away mode on.""" + await self._client.climate_command(key=self._static_info.key, + away=True) + + async def async_turn_away_mode_off(self) -> None: + """Turn away mode off.""" + await self._client.climate_command(key=self._static_info.key, + away=False) From 6463b8165f964dc7e068143d5e8b22a7e2f00ff5 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 10 Apr 2019 22:35:28 +0200 Subject: [PATCH 562/605] Fix deCONZ change entity_id bug (#22974) Fix deCONZ change entity_id bug --- homeassistant/components/deconz/deconz_device.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/deconz/deconz_device.py b/homeassistant/components/deconz/deconz_device.py index 0c5cbeef1fba92..6923c93dd6f7a0 100644 --- a/homeassistant/components/deconz/deconz_device.py +++ b/homeassistant/components/deconz/deconz_device.py @@ -26,10 +26,9 @@ async def async_added_to_hass(self): async def async_will_remove_from_hass(self) -> None: """Disconnect device object when removed.""" - if self.unsub_dispatcher is not None: - self.unsub_dispatcher() self._device.remove_callback(self.async_update_callback) - self._device = None + del self.gateway.deconz_ids[self.entity_id] + self.unsub_dispatcher() @callback def async_update_callback(self, reason): From 38d92b2abfa5a4ced530ecb809fa5e7c1ead568d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 10 Apr 2019 22:56:34 +0200 Subject: [PATCH 563/605] Fix optimistic mode and add tests (#22899) --- homeassistant/components/mqtt/lock.py | 12 ++++- tests/components/lock/common.py | 40 ++++++++++++++ tests/components/mqtt/test_lock.py | 77 +++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index b9c095a054aeeb..235eacc94540cf 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -79,12 +79,14 @@ class MqttLock(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, def __init__(self, config, config_entry, discovery_hash): """Initialize the lock.""" - self._config = config self._unique_id = config.get(CONF_UNIQUE_ID) self._state = False self._sub_state = None self._optimistic = False + # Load config + self._setup_from_config(config) + device_config = config.get(CONF_DEVICE) MqttAttributes.__init__(self, config) @@ -101,13 +103,19 @@ async def async_added_to_hass(self): async def discovery_update(self, discovery_payload): """Handle updated discovery message.""" config = PLATFORM_SCHEMA(discovery_payload) - self._config = config + self._setup_from_config(config) await self.attributes_discovery_update(config) await self.availability_discovery_update(config) await self.device_info_discovery_update(config) await self._subscribe_topics() self.async_write_ha_state() + def _setup_from_config(self, config): + """(Re)Setup the entity.""" + self._config = config + + self._optimistic = config[CONF_OPTIMISTIC] + async def _subscribe_topics(self): """(Re)Subscribe to topics.""" value_template = self._config.get(CONF_VALUE_TEMPLATE) diff --git a/tests/components/lock/common.py b/tests/components/lock/common.py index 2150b3cb894fd3..c5a71a3eb96a42 100644 --- a/tests/components/lock/common.py +++ b/tests/components/lock/common.py @@ -6,6 +6,7 @@ from homeassistant.components.lock import DOMAIN from homeassistant.const import ( ATTR_CODE, ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK, SERVICE_OPEN) +from homeassistant.core import callback from homeassistant.loader import bind_hass @@ -21,6 +22,19 @@ def lock(hass, entity_id=None, code=None): hass.services.call(DOMAIN, SERVICE_LOCK, data) +@callback +@bind_hass +def async_lock(hass, entity_id=None, code=None): + """Lock all or specified locks.""" + data = {} + if code: + data[ATTR_CODE] = code + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_LOCK, data)) + + @bind_hass def unlock(hass, entity_id=None, code=None): """Unlock all or specified locks.""" @@ -33,6 +47,19 @@ def unlock(hass, entity_id=None, code=None): hass.services.call(DOMAIN, SERVICE_UNLOCK, data) +@callback +@bind_hass +def async_unlock(hass, entity_id=None, code=None): + """Lock all or specified locks.""" + data = {} + if code: + data[ATTR_CODE] = code + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_UNLOCK, data)) + + @bind_hass def open_lock(hass, entity_id=None, code=None): """Open all or specified locks.""" @@ -43,3 +70,16 @@ def open_lock(hass, entity_id=None, code=None): data[ATTR_ENTITY_ID] = entity_id hass.services.call(DOMAIN, SERVICE_OPEN, data) + + +@callback +@bind_hass +def async_open_lock(hass, entity_id=None, code=None): + """Lock all or specified locks.""" + data = {} + if code: + data[ATTR_CODE] = code + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_OPEN, data)) diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index 52dd3ecfbdbebe..cc629b2165deeb 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -11,6 +11,7 @@ from tests.common import ( MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component, mock_registry) +from tests.components.lock import common async def test_controlling_state_via_topic(hass, mqtt_mock): @@ -75,6 +76,82 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock): assert state.state is STATE_UNLOCKED +async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): + """Test optimistic mode without state topic.""" + assert await async_setup_component(hass, lock.DOMAIN, { + lock.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'command_topic': 'command-topic', + 'payload_lock': 'LOCK', + 'payload_unlock': 'UNLOCK' + } + }) + + state = hass.states.get('lock.test') + assert state.state is STATE_UNLOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + common.async_lock(hass, 'lock.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'command-topic', 'LOCK', 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('lock.test') + assert state.state is STATE_LOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + common.async_unlock(hass, 'lock.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'command-topic', 'UNLOCK', 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('lock.test') + assert state.state is STATE_UNLOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + +async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock): + """Test optimistic mode without state topic.""" + assert await async_setup_component(hass, lock.DOMAIN, { + lock.DOMAIN: { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'payload_lock': 'LOCK', + 'payload_unlock': 'UNLOCK', + 'optimistic': True + } + }) + + state = hass.states.get('lock.test') + assert state.state is STATE_UNLOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + common.async_lock(hass, 'lock.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'command-topic', 'LOCK', 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('lock.test') + assert state.state is STATE_LOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + common.async_unlock(hass, 'lock.test') + await hass.async_block_till_done() + + mqtt_mock.async_publish.assert_called_once_with( + 'command-topic', 'UNLOCK', 0, False) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get('lock.test') + assert state.state is STATE_UNLOCKED + assert state.attributes.get(ATTR_ASSUMED_STATE) + + async def test_default_availability_payload(hass, mqtt_mock): """Test availability by default payload with defined topic.""" assert await async_setup_component(hass, lock.DOMAIN, { From 7862fdd27eafb094f38b697feb761f1c29e9e29f Mon Sep 17 00:00:00 2001 From: ehendrix23 Date: Wed, 10 Apr 2019 15:24:12 -0600 Subject: [PATCH 564/605] Fix myq increasing number of network connections (#22432) * Fix for network issues Fix for network issues * Further changes to network connection * websession is created in pymyq websession is created in pymyq instead. Added call on stop event to close web session. * Updated requirements file * Added comment * Changed back to use aiohttp_client * Cleanup closed sockets in aiohttp Enable automatic cleanup of closed sockets in aiohttp client helper. * Updated manifest & requirements * Updated comment block --- homeassistant/components/myq/cover.py | 9 +++++---- homeassistant/components/myq/manifest.json | 2 +- homeassistant/helpers/aiohttp_client.py | 5 ++++- requirements_all.txt | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/myq/cover.py b/homeassistant/components/myq/cover.py index b1112f153b23a8..5b926a183f72c9 100644 --- a/homeassistant/components/myq/cover.py +++ b/homeassistant/components/myq/cover.py @@ -1,16 +1,17 @@ """Support for MyQ-Enabled Garage Doors.""" import logging - import voluptuous as vol from homeassistant.components.cover import ( - PLATFORM_SCHEMA, SUPPORT_CLOSE, SUPPORT_OPEN, CoverDevice) + CoverDevice, PLATFORM_SCHEMA, SUPPORT_CLOSE, SUPPORT_OPEN +) from homeassistant.const import ( CONF_PASSWORD, CONF_TYPE, CONF_USERNAME, STATE_CLOSED, STATE_CLOSING, - STATE_OPEN, STATE_OPENING) + STATE_OPEN, STATE_OPENING +) from homeassistant.helpers import aiohttp_client, config_validation as cv -REQUIREMENTS = ['pymyq==1.1.0'] +REQUIREMENTS = ['pymyq==1.2.0'] _LOGGER = logging.getLogger(__name__) MYQ_TO_HASS = { diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json index 3dbabd4260dacf..c4057fecb25d51 100644 --- a/homeassistant/components/myq/manifest.json +++ b/homeassistant/components/myq/manifest.json @@ -3,7 +3,7 @@ "name": "Myq", "documentation": "https://www.home-assistant.io/components/myq", "requirements": [ - "pymyq==1.1.0" + "pymyq==1.2.0" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 6b1dd10bd5bd16..f5b3e443d3a379 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -168,7 +168,10 @@ def _async_get_connector(hass: HomeAssistantType, else: ssl_context = False - connector = aiohttp.TCPConnector(loop=hass.loop, ssl=ssl_context) + connector = aiohttp.TCPConnector(loop=hass.loop, + enable_cleanup_closed=True, + ssl=ssl_context, + ) hass.data[key] = connector async def _async_close_connector(event: Event) -> None: diff --git a/requirements_all.txt b/requirements_all.txt index dd5aec398c7bcc..9738d5e0191965 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1160,7 +1160,7 @@ pymonoprice==0.3 pymusiccast==0.1.6 # homeassistant.components.myq -pymyq==1.1.0 +pymyq==1.2.0 # homeassistant.components.mysensors pymysensors==0.18.0 From 153c6957b9a9d9436eeef02c87a4b71f618dfbc9 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 10 Apr 2019 15:25:19 -0600 Subject: [PATCH 565/605] Add watchdog reset to on_connect in Ambient (#22956) --- homeassistant/components/ambient_station/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 6dee4637a96ae5..944d4e14e7d26d 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -329,6 +329,8 @@ def on_connect(): """Define a handler to fire when the websocket is connected.""" _LOGGER.info('Connected to websocket') _LOGGER.debug('Watchdog starting') + if self._watchdog_listener: + self._watchdog_listener() self._watchdog_listener = async_call_later( self._hass, DEFAULT_WATCHDOG_SECONDS, _ws_reconnect) From c81b1956dafd090c0a62a2e015a72428d2c18870 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 10 Apr 2019 15:01:14 -0700 Subject: [PATCH 566/605] Updated frontend to 20190410.0 --- homeassistant/components/frontend/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index f0358dbd6cc8d6..cfee41dc6ae806 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190331.0'] +REQUIREMENTS = ['home-assistant-frontend==20190410.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', From e9d9861bda7f34c8367be292179d68b2bd9c2115 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 10 Apr 2019 15:01:32 -0700 Subject: [PATCH 567/605] Update translations --- .../components/auth/.translations/nn.json | 2 +- .../components/axis/.translations/lb.json | 5 +++ .../components/axis/.translations/nn.json | 13 ++++++++ .../components/deconz/.translations/lb.json | 8 +++++ .../components/deconz/.translations/nn.json | 2 +- .../components/esphome/.translations/nn.json | 9 ++++++ .../components/esphome/.translations/pl.json | 2 +- .../components/esphome/.translations/uk.json | 2 +- .../components/hangouts/.translations/lb.json | 3 +- .../components/hangouts/.translations/nn.json | 2 +- .../components/heos/.translations/lb.json | 20 ++++++++++++ .../components/heos/.translations/nn.json | 12 +++++++ .../homekit_controller/.translations/nn.json | 12 +++++++ .../homematicip_cloud/.translations/nn.json | 2 +- .../homematicip_cloud/.translations/no.json | 2 +- .../components/ifttt/.translations/nn.json | 10 ++++++ .../logi_circle/.translations/ko.json | 32 +++++++++++++++++++ .../logi_circle/.translations/lb.json | 32 +++++++++++++++++++ .../logi_circle/.translations/nn.json | 19 +++++++++++ .../logi_circle/.translations/no.json | 26 +++++++++++++++ .../mobile_app/.translations/nn.json | 10 ++++++ .../moon/.translations/sensor.lb.json | 8 ++++- .../components/nest/.translations/ko.json | 4 +-- .../owntracks/.translations/da.json | 2 +- .../components/point/.translations/ko.json | 4 +-- .../components/point/.translations/lb.json | 2 +- .../components/ps4/.translations/nn.json | 13 ++++++++ .../components/ps4/.translations/no.json | 6 ++-- .../rainmachine/.translations/nn.json | 5 +++ .../season/.translations/sensor.nn.json | 2 -- .../components/sonos/.translations/nn.json | 2 +- .../tellduslive/.translations/lb.json | 2 +- .../components/toon/.translations/ko.json | 2 +- .../components/toon/.translations/nn.json | 5 +++ .../components/tradfri/.translations/nn.json | 2 +- .../components/upnp/.translations/nn.json | 16 ++++++++++ .../components/upnp/.translations/ru.json | 2 +- .../components/zwave/.translations/nn.json | 9 ++++++ 38 files changed, 286 insertions(+), 25 deletions(-) create mode 100644 homeassistant/components/axis/.translations/nn.json create mode 100644 homeassistant/components/esphome/.translations/nn.json create mode 100644 homeassistant/components/heos/.translations/lb.json create mode 100644 homeassistant/components/heos/.translations/nn.json create mode 100644 homeassistant/components/homekit_controller/.translations/nn.json create mode 100644 homeassistant/components/ifttt/.translations/nn.json create mode 100644 homeassistant/components/logi_circle/.translations/ko.json create mode 100644 homeassistant/components/logi_circle/.translations/lb.json create mode 100644 homeassistant/components/logi_circle/.translations/nn.json create mode 100644 homeassistant/components/logi_circle/.translations/no.json create mode 100644 homeassistant/components/mobile_app/.translations/nn.json create mode 100644 homeassistant/components/ps4/.translations/nn.json create mode 100644 homeassistant/components/rainmachine/.translations/nn.json create mode 100644 homeassistant/components/toon/.translations/nn.json create mode 100644 homeassistant/components/upnp/.translations/nn.json create mode 100644 homeassistant/components/zwave/.translations/nn.json diff --git a/homeassistant/components/auth/.translations/nn.json b/homeassistant/components/auth/.translations/nn.json index 24d756f938bc51..346c1cfe0c7edf 100644 --- a/homeassistant/components/auth/.translations/nn.json +++ b/homeassistant/components/auth/.translations/nn.json @@ -6,7 +6,7 @@ }, "step": { "init": { - "description": "For \u00e5 aktivere tofaktorautentisering ved hjelp av tidsbaserte eingangspassord, skann QR-koden med autentiseringsappen din. Dersom du ikkje har ein, vil vi r\u00e5de deg til \u00e5 bruke anten [Google Authenticator] (https://support.google.com/accounts/answer/1066447) eller [Authy] (https://authy.com/). \n\n {qr_code} \n \nN\u00e5r du har skanna koda, skriv du inn den sekssifra koda fr\u00e5 appen din for \u00e5 stadfeste oppsettet. Dersom du har problemer med \u00e5 skanne QR-koda, gjer du eit manuelt oppsett med kode ** ` {code} ` **.", + "description": "For \u00e5 aktivere to-faktor-autentisering ved hjelp av tid-baserte eingangspassord, skann QR-koden med autentiseringsappen din. Dersom du ikkje har ein, vil vi r\u00e5de deg til \u00e5 bruke anten [Google Authenticator] (https://support.google.com/accounts/answer/1066447) eller [Authy] (https://authy.com/). \n\n {qr_code} \n \nN\u00e5r du har skanna koda, skriv du inn den sekssifra koda fr\u00e5 appen din for \u00e5 stadfeste oppsettet. Dersom du har problemer med \u00e5 skanne QR-koda, gjer du eit manuelt oppsett med kode ** ` {code} ` **.", "title": "Konfigurer to-faktor-autentisering ved hjelp av TOTP" } }, diff --git a/homeassistant/components/axis/.translations/lb.json b/homeassistant/components/axis/.translations/lb.json index e0f6ebc1553535..6b0728f4030d86 100644 --- a/homeassistant/components/axis/.translations/lb.json +++ b/homeassistant/components/axis/.translations/lb.json @@ -1,5 +1,10 @@ { "config": { + "abort": { + "already_configured": "Apparat ass scho konfigur\u00e9iert", + "bad_config_file": "Feelerhaft Donn\u00e9e\u00eb aus der Konfiguratioun's Datei", + "link_local_address": "Lokal Link Adressen ginn net \u00ebnnerst\u00ebtzt" + }, "error": { "already_configured": "Apparat ass scho konfigur\u00e9iert", "device_unavailable": "Apparat ass net erreechbar", diff --git a/homeassistant/components/axis/.translations/nn.json b/homeassistant/components/axis/.translations/nn.json new file mode 100644 index 00000000000000..3364446935953a --- /dev/null +++ b/homeassistant/components/axis/.translations/nn.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Vert", + "password": "Passord", + "port": "Port" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/.translations/lb.json b/homeassistant/components/deconz/.translations/lb.json index fc05bb8b94e06b..2e4e38668d1f67 100644 --- a/homeassistant/components/deconz/.translations/lb.json +++ b/homeassistant/components/deconz/.translations/lb.json @@ -9,6 +9,14 @@ "no_key": "Konnt keen API Schl\u00ebssel kr\u00e9ien" }, "step": { + "hassio_confirm": { + "data": { + "allow_clip_sensor": "Erlaabt den Import vun virtuellen Sensoren", + "allow_deconz_groups": "Erlaabt den Import vun deCONZ Gruppen" + }, + "description": "W\u00ebllt dir Home Assistant konfigur\u00e9iere fir sech mat der deCONZ gateway ze verbannen d\u00e9i vum hass.io add-on {addon} bereet gestallt g\u00ebtt?", + "title": "deCONZ Zigbee gateway via Hass.io add-on" + }, "init": { "data": { "host": "Host", diff --git a/homeassistant/components/deconz/.translations/nn.json b/homeassistant/components/deconz/.translations/nn.json index 4bdc4b4c1bee38..46933ced42761f 100644 --- a/homeassistant/components/deconz/.translations/nn.json +++ b/homeassistant/components/deconz/.translations/nn.json @@ -23,7 +23,7 @@ "options": { "data": { "allow_clip_sensor": "Tillat importering av virtuelle sensorar", - "allow_deconz_groups": "Tillat importering av deCONZ-grupper" + "allow_deconz_groups": "Tillat \u00e5 importera deCONZ-grupper" }, "title": "Ekstra konfigurasjonsalternativ for deCONZ" } diff --git a/homeassistant/components/esphome/.translations/nn.json b/homeassistant/components/esphome/.translations/nn.json new file mode 100644 index 00000000000000..830391f58f6e30 --- /dev/null +++ b/homeassistant/components/esphome/.translations/nn.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "discovery_confirm": { + "title": "Fann ESPhome node" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/.translations/pl.json b/homeassistant/components/esphome/.translations/pl.json index d24fb929068c1c..5693efde9a8d50 100644 --- a/homeassistant/components/esphome/.translations/pl.json +++ b/homeassistant/components/esphome/.translations/pl.json @@ -18,7 +18,7 @@ }, "discovery_confirm": { "description": "Czy chcesz doda\u0107 w\u0119ze\u0142 ESPHome `{name}` do Home Assistant?", - "title": "Znaleziono w\u0119ze\u0142 ESPHome " + "title": "Znaleziono w\u0119ze\u0142 ESPHome" }, "user": { "data": { diff --git a/homeassistant/components/esphome/.translations/uk.json b/homeassistant/components/esphome/.translations/uk.json index 4f4c2f32c6157f..79c9e70bcc84d8 100644 --- a/homeassistant/components/esphome/.translations/uk.json +++ b/homeassistant/components/esphome/.translations/uk.json @@ -18,7 +18,7 @@ }, "discovery_confirm": { "description": "\u0414\u043e\u0434\u0430\u0442\u0438 ESPHome \u0432\u0443\u0437\u043e\u043b {name} \u0443 Home Assistant?", - "title": "\u0412\u0438\u044f\u0432\u043b\u0435\u043d\u043e \u0432\u0443\u0437\u043e\u043b ESPHome " + "title": "\u0412\u0438\u044f\u0432\u043b\u0435\u043d\u043e \u0432\u0443\u0437\u043e\u043b ESPHome" }, "user": { "data": { diff --git a/homeassistant/components/hangouts/.translations/lb.json b/homeassistant/components/hangouts/.translations/lb.json index 426ab689626196..c22b02fd7ed38e 100644 --- a/homeassistant/components/hangouts/.translations/lb.json +++ b/homeassistant/components/hangouts/.translations/lb.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Google Hangouts ass scho konfigur\u00e9iert", - "unknown": "Onbekannten Fehler opgetrueden" + "unknown": "Onbekannten Feeler opgetrueden" }, "error": { "invalid_2fa": "Ong\u00eblteg 2-Faktor Authentifikatioun, prob\u00e9iert w.e.g. nach emol.", @@ -19,6 +19,7 @@ }, "user": { "data": { + "authorization_code": "Autorisatioun's Code (n\u00e9ideg fir eng manuell Authentifikatioun)", "email": "E-Mail Adress", "password": "Passwuert" }, diff --git a/homeassistant/components/hangouts/.translations/nn.json b/homeassistant/components/hangouts/.translations/nn.json index 58e5f4f45fdc56..c8a5fb4481b8c1 100644 --- a/homeassistant/components/hangouts/.translations/nn.json +++ b/homeassistant/components/hangouts/.translations/nn.json @@ -14,7 +14,7 @@ "data": { "2fa": "2FA PIN" }, - "title": "To-faktor-autentiserin" + "title": "To-faktor-autentisering" }, "user": { "data": { diff --git a/homeassistant/components/heos/.translations/lb.json b/homeassistant/components/heos/.translations/lb.json new file mode 100644 index 00000000000000..4b536e3ff4eab4 --- /dev/null +++ b/homeassistant/components/heos/.translations/lb.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_setup": "Dir k\u00ebnnt n\u00ebmmen eng eenzeg Heos Verbindung konfigur\u00e9ieren, well se all Apparater am Netzwierk \u00ebnnerst\u00ebtzen." + }, + "error": { + "connection_failure": "Kann sech net mat dem spezifiz\u00e9ierten Apparat verbannen." + }, + "step": { + "user": { + "data": { + "access_token": "Apparat" + }, + "description": "Gitt den Numm oder IP-Adress vun engem Heos-Apparat an (am beschten iwwer Kabel mam Reseau verbonnen).", + "title": "Mat Heos verbannen" + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/heos/.translations/nn.json b/homeassistant/components/heos/.translations/nn.json new file mode 100644 index 00000000000000..ec2dc294500115 --- /dev/null +++ b/homeassistant/components/heos/.translations/nn.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "access_token": "Vert" + } + } + }, + "title": "Heos" + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/.translations/nn.json b/homeassistant/components/homekit_controller/.translations/nn.json new file mode 100644 index 00000000000000..995d67792389d7 --- /dev/null +++ b/homeassistant/components/homekit_controller/.translations/nn.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "pair": { + "data": { + "pairing_code": "Paringskode" + } + } + }, + "title": "HomeKit tilbeh\u00f8r" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/nn.json b/homeassistant/components/homematicip_cloud/.translations/nn.json index 966c827c89d2c4..da375563d917dc 100644 --- a/homeassistant/components/homematicip_cloud/.translations/nn.json +++ b/homeassistant/components/homematicip_cloud/.translations/nn.json @@ -21,7 +21,7 @@ "title": "Vel HomematicIP tilgangspunkt" }, "link": { - "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og sendknappen for \u00e5 registrere HomematicIP med Home Assitant.\n\n ! [Plassering av knapp p\u00e5 bro] (/ static / images / config_flows / config_homematicip_cloud.png)", + "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og sendknappen for \u00e5 registrere HomematicIP med Home Assistant.\n\n ! [Plassering av knapp p\u00e5 bro] (/ static / images / config_flows / config_homematicip_cloud.png)", "title": "Link tilgangspunk" } }, diff --git a/homeassistant/components/homematicip_cloud/.translations/no.json b/homeassistant/components/homematicip_cloud/.translations/no.json index d9e6636c9720fa..28cfc502aba4e2 100644 --- a/homeassistant/components/homematicip_cloud/.translations/no.json +++ b/homeassistant/components/homematicip_cloud/.translations/no.json @@ -25,6 +25,6 @@ "title": "Link tilgangspunkt" } }, - "title": "HomematicIP Sky" + "title": "HomematicIP Cloud" } } \ No newline at end of file diff --git a/homeassistant/components/ifttt/.translations/nn.json b/homeassistant/components/ifttt/.translations/nn.json new file mode 100644 index 00000000000000..e3bef7270e5224 --- /dev/null +++ b/homeassistant/components/ifttt/.translations/nn.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "user": { + "description": "Er du sikker p\u00e5 at du \u00f8nskjer \u00e5 setta opp IFTTT?" + } + }, + "title": "IFTTT" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/ko.json b/homeassistant/components/logi_circle/.translations/ko.json new file mode 100644 index 00000000000000..577f3475b58f33 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/ko.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_setup": "\ud558\ub098\uc758 Logi Circle \uacc4\uc815\ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "external_error": "\ub2e4\ub978 Flow \uc5d0\uc11c \uc608\uc678\uc0ac\ud56d\uc774 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", + "external_setup": "Logi Circle \uc774 \ub2e4\ub978 Flow \uc5d0\uc11c \uc131\uacf5\uc801\uc73c\ub85c \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "no_flows": "Logi Circle \uc744 \uc778\uc99d\ud558\ub824\uba74 \uba3c\uc800 Logi Circle \uc744 \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/logi_circle/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." + }, + "create_entry": { + "default": "Logi Circle \ub85c \uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "error": { + "auth_error": "API \uc2b9\uc778\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4.", + "auth_timeout": "\uc5d1\uc138\uc2a4 \ud1a0\ud070 \uc694\uccad\uc911 \uc2b9\uc778 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "follow_link": "Submit \ubc84\ud2bc\uc744 \ub204\ub974\uae30 \uc804\uc5d0 \ub9c1\ud06c\ub97c \ub530\ub77c \uc778\uc99d\uc744 \ubc1b\uc544\uc8fc\uc138\uc694" + }, + "step": { + "auth": { + "description": "\uc544\ub798 \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec Logi Circle \uacc4\uc815\uc5d0 \ub300\ud574 \ub3d9\uc758 \ud55c \ub2e4\uc74c, \ub2e4\uc2dc \ub3cc\uc544\uc640\uc11c \ud558\ub2e8\uc758 Submit \ubc84\ud2bc\uc744 \ub20c\ub7ec\uc8fc\uc138\uc694.\n\n[\ub9c1\ud06c]({authorization_url})", + "title": "Logi Circle \uc778\uc99d" + }, + "user": { + "data": { + "flow_impl": "\uacf5\uae09\uc790" + }, + "description": "Logi Circle \uc744 \uc778\uc99d\ud558\uae30 \uc704\ud55c \uc778\uc99d \uacf5\uae09\uc790\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", + "title": "\uc778\uc99d \uacf5\uae09\uc790" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/lb.json b/homeassistant/components/logi_circle/.translations/lb.json new file mode 100644 index 00000000000000..b0befa80fd4ff3 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/lb.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_setup": "Dir k\u00ebnnt n\u00ebmmen een eenzegen Logi Circle Kont konfigur\u00e9ieren.", + "external_error": "Ausnam vun engem anere Floss.", + "external_setup": "Logi Circle gouf vun engem anere Floss erfollegr\u00e4ich konfigur\u00e9iert.", + "no_flows": "Dir musst Logi Circle konfigur\u00e9ieren, ier Dir d\u00ebs Authentifiz\u00e9ierung k\u00ebnnt benotzen.[Liest w.e.g. d'Instruktioune](https://www.home-assistant.io/components/logi_circle/)." + }, + "create_entry": { + "default": "Erfollegr\u00e4ich mat Logi Circle authentifiz\u00e9iert." + }, + "error": { + "auth_error": "Feeler bei der API Autorisatioun.", + "auth_timeout": "Z\u00e4it Iwwerschreidung vun der Autorisatioun beim ufroe vum Acc\u00e8s Jeton.", + "follow_link": "Follegt w.e.g dem Link an authentifiz\u00e9iert iech ier de op Ofsch\u00e9cken dr\u00e9ckt." + }, + "step": { + "auth": { + "description": "Follegt dem Link \u00ebnnendr\u00ebnner an accept\u00e9iert den Acc\u00e8s zu \u00e4rem Logi Circle Kont , a kommt dann zer\u00e9ck heihin an dr\u00e9ck op ofsch\u00e9cken hei \u00ebnnen.\n\n[Link]({authorization_url})", + "title": "Mat Logi Circle authentifiz\u00e9ieren" + }, + "user": { + "data": { + "flow_impl": "Ubidder" + }, + "description": "Wielt den Authentifikatioun Ubidder deen sech mat Logi Circle verbanne soll.", + "title": "Authentifikatioun Ubidder" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/nn.json b/homeassistant/components/logi_circle/.translations/nn.json new file mode 100644 index 00000000000000..0ea648256d3f49 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/nn.json @@ -0,0 +1,19 @@ +{ + "config": { + "create_entry": { + "default": "Vellukka autentisering med Logi Circle" + }, + "error": { + "auth_error": "API-autorisasjonen mislyktes." + }, + "step": { + "auth": { + "title": "Godkjenn med Logi Circle" + }, + "user": { + "description": "Vel kva for ein autentiseringsleverand\u00f8r du vil godkjenne med Logi Circle" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/logi_circle/.translations/no.json b/homeassistant/components/logi_circle/.translations/no.json new file mode 100644 index 00000000000000..1ab30f14313b12 --- /dev/null +++ b/homeassistant/components/logi_circle/.translations/no.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "external_error": "Det oppstod et unntak fra en annen flyt." + }, + "create_entry": { + "default": "Vellykket autentisering med Logi Circle" + }, + "error": { + "auth_error": "API-autorisasjonen mislyktes." + }, + "step": { + "auth": { + "title": "Godkjenn med Logi Circle" + }, + "user": { + "data": { + "flow_impl": "Tilbyder" + }, + "description": "Velg med hvilken autentiseringsleverand\u00f8r du vil godkjenne Logi Circle.", + "title": "Autentiseringsleverand\u00f8r" + } + }, + "title": "Logi Circle" + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/.translations/nn.json b/homeassistant/components/mobile_app/.translations/nn.json new file mode 100644 index 00000000000000..b4494a45ad25a2 --- /dev/null +++ b/homeassistant/components/mobile_app/.translations/nn.json @@ -0,0 +1,10 @@ +{ + "config": { + "step": { + "confirm": { + "title": "Mobilapp" + } + }, + "title": "Mobilapp" + } +} \ No newline at end of file diff --git a/homeassistant/components/moon/.translations/sensor.lb.json b/homeassistant/components/moon/.translations/sensor.lb.json index d2f95685634309..174d1fdcc13bca 100644 --- a/homeassistant/components/moon/.translations/sensor.lb.json +++ b/homeassistant/components/moon/.translations/sensor.lb.json @@ -1,6 +1,12 @@ { "state": { + "first_quarter": "zouhuelend", "full_moon": "Vollmound", - "new_moon": "Neimound" + "last_quarter": "ofhuelend", + "new_moon": "Neimound", + "waning_crescent": "ofhuelend hallef", + "waning_gibbous": "ofhuelend dr\u00e4i v\u00e9ierels", + "waxing_crescent": "zouhuelend hallef", + "waxing_gibbous": "zouhuelend dr\u00e4i v\u00e9ierels" } } \ No newline at end of file diff --git a/homeassistant/components/nest/.translations/ko.json b/homeassistant/components/nest/.translations/ko.json index a53a26bca5a174..42170910d14af2 100644 --- a/homeassistant/components/nest/.translations/ko.json +++ b/homeassistant/components/nest/.translations/ko.json @@ -4,7 +4,7 @@ "already_setup": "\ud558\ub098\uc758 Nest \uacc4\uc815\ub9cc \uad6c\uc131 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "authorize_url_fail": "\uc778\uc99d url \uc0dd\uc131\uc5d0 \uc54c \uc218 \uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", "authorize_url_timeout": "\uc778\uc99d url \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", - "no_flows": "Nest \ub97c \uc778\uc99d\ud558\uae30 \uc804\uc5d0 Nest \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/nest/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." + "no_flows": "Nest \ub97c \uc778\uc99d\ud558\ub824\uba74 \uba3c\uc800 Nest \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/nest/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." }, "error": { "internal_error": "\ucf54\ub4dc \uc720\ud6a8\uc131 \uac80\uc0ac\uc5d0 \ub0b4\ubd80 \uc624\ub958 \ubc1c\uc0dd", @@ -17,7 +17,7 @@ "data": { "flow_impl": "\uacf5\uae09\uc790" }, - "description": "Nest\ub85c \uc778\uc99d\ud558\ub824\ub294 \uc778\uc99d \uacf5\uae09\uc790\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", + "description": "Nest \ub97c \uc778\uc99d\ud558\uae30 \uc704\ud55c \uc778\uc99d \uacf5\uae09\uc790\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", "title": "\uc778\uc99d \uacf5\uae09\uc790" }, "link": { diff --git a/homeassistant/components/owntracks/.translations/da.json b/homeassistant/components/owntracks/.translations/da.json index 7f4053f8ead714..bc1328d57e4cde 100644 --- a/homeassistant/components/owntracks/.translations/da.json +++ b/homeassistant/components/owntracks/.translations/da.json @@ -4,7 +4,7 @@ "one_instance_allowed": "Det er kun n\u00f8dvendigt med en ops\u00e6tning" }, "create_entry": { - "default": "\n\n P\u00e5 Android skal du \u00e5bne [OwnTracks applikationen]({android_url}), g\u00e5 til indstillinger -> forbindelse. Skift f\u00f8lgende indstillinger: \n - Tilstand: Privat HTTP\n - V\u00e6rt: {webhook_url}\n - Identifikation:\n - Brugernavn: ` ` \n - Enheds-id: ` ` \n\n P\u00e5 iOS skal du \u00e5bne [OwnTracks applikationen]({ios_url}), tryk p\u00e5 (i) ikonet \u00f8verst til venstre -> indstillinger. Skift f\u00f8lgende indstillinger: \n - Tilstand: HTTP\n - URL: {webhook_url}\n - Aktiver godkendelse \n - Bruger ID: ` ` \n\n {secret}\n \n Se [dokumentationen]({docs_url}) for at f\u00e5 flere oplysninger." + "default": "\n\nP\u00e5 Android skal du \u00e5bne [OwnTracks applikationen]({android_url}), g\u00e5 til indstillinger -> forbindelse. Skift f\u00f8lgende indstillinger: \n - Tilstand: Privat HTTP\n - V\u00e6rt: {webhook_url}\n - Identifikation:\n - Brugernavn: ` ` \n - Enheds-id: ` ` \n\nP\u00e5 iOS skal du \u00e5bne [OwnTracks applikationen]({ios_url}), tryk p\u00e5 (i) ikonet \u00f8verst til venstre -> indstillinger. Skift f\u00f8lgende indstillinger: \n - Tilstand: HTTP\n - URL: {webhook_url}\n - Aktiver godkendelse \n - Bruger ID: ` ` \n\n {secret}\n \n Se [dokumentationen]({docs_url}) for at f\u00e5 flere oplysninger." }, "step": { "user": { diff --git a/homeassistant/components/point/.translations/ko.json b/homeassistant/components/point/.translations/ko.json index 8ffcbab1ecc89e..d70859c8bde0d6 100644 --- a/homeassistant/components/point/.translations/ko.json +++ b/homeassistant/components/point/.translations/ko.json @@ -5,7 +5,7 @@ "authorize_url_fail": "\uc778\uc99d url \uc0dd\uc131\uc5d0 \uc54c \uc218 \uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.", "authorize_url_timeout": "\uc778\uc99d url \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "external_setup": "Point \uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.", - "no_flows": "Point \ub97c \uc778\uc99d\ud558\uae30 \uc804\uc5d0 Point \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/point/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." + "no_flows": "Point \ub97c \uc778\uc99d\ud558\ub824\uba74 \uba3c\uc800 Point \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/point/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694." }, "create_entry": { "default": "Point \uae30\uae30\ub294 Minut \ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" @@ -23,7 +23,7 @@ "data": { "flow_impl": "\uacf5\uae09\uc790" }, - "description": "Point\ub85c \uc778\uc99d\ud558\ub824\ub294 \uc778\uc99d \uacf5\uae09\uc790\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", + "description": "Point \ub97c \uc778\uc99d\ud558\uae30 \uc704\ud55c \uc778\uc99d \uacf5\uae09\uc790\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", "title": "\uc778\uc99d \uacf5\uae09\uc790" } }, diff --git a/homeassistant/components/point/.translations/lb.json b/homeassistant/components/point/.translations/lb.json index 571f4617215783..ea589a2c3d352d 100644 --- a/homeassistant/components/point/.translations/lb.json +++ b/homeassistant/components/point/.translations/lb.json @@ -16,7 +16,7 @@ }, "step": { "auth": { - "description": "Follegt dem Link \u00ebnnendr\u00ebnner an accept\u00e9iert den Acc\u00e8s zu \u00e4rem Minut Kont , dann kommt zer\u00e9ck heihin an dr\u00e9ck op ofsch\u00e9cken hei \u00ebnnen.\n\n[Link]({authorization_url})", + "description": "Follegt dem Link \u00ebnnendr\u00ebnner an accept\u00e9iert den Acc\u00e8s zu \u00e4rem Minut Kont , a kommt dann zer\u00e9ck heihin an dr\u00e9ck op ofsch\u00e9cken hei \u00ebnnen.\n\n[Link]({authorization_url})", "title": "Point authentifiz\u00e9ieren" }, "user": { diff --git a/homeassistant/components/ps4/.translations/nn.json b/homeassistant/components/ps4/.translations/nn.json new file mode 100644 index 00000000000000..b3302389c88c9a --- /dev/null +++ b/homeassistant/components/ps4/.translations/nn.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "port_987_bind_error": "Kunne ikkje binda til port 987. Sj\u00e5 [dokumentasjonen](https://www.home-assistant.io/components/ps4/) for meir informasjon.", + "port_997_bind_error": "Kunne ikkje binde til port 997. Sj\u00e5 [dokumentasjonen] (https://www.home-assistant.io/components/ps4/) for ytterlegare informasjon." + }, + "step": { + "mode": { + "title": "Playstation 4" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ps4/.translations/no.json b/homeassistant/components/ps4/.translations/no.json index 8907032d83e30e..ea2c0b37f6e1e1 100644 --- a/homeassistant/components/ps4/.translations/no.json +++ b/homeassistant/components/ps4/.translations/no.json @@ -4,8 +4,8 @@ "credential_error": "Feil ved henting av legitimasjon.", "devices_configured": "Alle enheter som ble funnet er allerede konfigurert.", "no_devices_found": "Ingen PlayStation 4 enheter funnet p\u00e5 nettverket.", - "port_987_bind_error": "Kunne ikke binde til port 987.", - "port_997_bind_error": "Kunne ikke binde til port 997." + "port_987_bind_error": "Kunne ikke binde til port 987. Se [dokumentasjonen](https://www.home-assistant.io/components/ps4/) for mer info.", + "port_997_bind_error": "Kunne ikke binde til port 997. Se [dokumentasjonen] (https://www.home-assistant.io/components/ps4/) for videre informasjon." }, "error": { "login_failed": "Klarte ikke \u00e5 koble til PlayStation 4. Bekreft at PIN koden er riktig.", @@ -14,7 +14,7 @@ }, "step": { "creds": { - "description": "Legitimasjon n\u00f8dvendig. Trykk \"Send\" og deretter i PS4-ens andre skjerm app, kan du oppdatere enheter, og velg \"Home-Assistent' enheten for \u00e5 fortsette.", + "description": "Legitimasjon n\u00f8dvendig. Trykk \"Send\" og deretter i PS4-ens andre skjerm app, kan du oppdatere enheter, og velg \"Home-Assistant' enheten for \u00e5 fortsette.", "title": "PlayStation 4" }, "link": { diff --git a/homeassistant/components/rainmachine/.translations/nn.json b/homeassistant/components/rainmachine/.translations/nn.json new file mode 100644 index 00000000000000..14b3c7e4dc48e5 --- /dev/null +++ b/homeassistant/components/rainmachine/.translations/nn.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "RainMachine" + } +} \ No newline at end of file diff --git a/homeassistant/components/season/.translations/sensor.nn.json b/homeassistant/components/season/.translations/sensor.nn.json index dbcff7ef81970a..3e8f626c17242c 100644 --- a/homeassistant/components/season/.translations/sensor.nn.json +++ b/homeassistant/components/season/.translations/sensor.nn.json @@ -1,7 +1,5 @@ { "state": { - "autumn": "Haust", - "spring": "V\u00e5r", "summer": "Sommar", "winter": "Vinter" } diff --git a/homeassistant/components/sonos/.translations/nn.json b/homeassistant/components/sonos/.translations/nn.json index f2451efaff42cd..e7df1f23f208ef 100644 --- a/homeassistant/components/sonos/.translations/nn.json +++ b/homeassistant/components/sonos/.translations/nn.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Det vart ikkje funne noko Sonoseiningar p\u00e5 nettverket.", + "no_devices_found": "Det vart ikkje funne noko Sonos-einingar p\u00e5 nettverket.", "single_instance_allowed": "Du treng berre \u00e5 sette opp \u00e9in Sonos-konfigurasjon." }, "step": { diff --git a/homeassistant/components/tellduslive/.translations/lb.json b/homeassistant/components/tellduslive/.translations/lb.json index 5eb4d1b978af18..4584635066c920 100644 --- a/homeassistant/components/tellduslive/.translations/lb.json +++ b/homeassistant/components/tellduslive/.translations/lb.json @@ -5,7 +5,7 @@ "already_setup": "TelldusLive ass scho konfigur\u00e9iert", "authorize_url_fail": "Onbekannte Feeler beim gener\u00e9ieren vun der Autorisatiouns URL.", "authorize_url_timeout": "Z\u00e4it Iwwerschreidung beim gener\u00e9ieren vun der Autorisatiouns URL.", - "unknown": "Onbekannten Fehler opgetrueden" + "unknown": "Onbekannten Feeler opgetrueden" }, "error": { "auth_error": "Feeler bei der Authentifikatioun, prob\u00e9iert w.e.g. nach emol" diff --git a/homeassistant/components/toon/.translations/ko.json b/homeassistant/components/toon/.translations/ko.json index 3a0698aed8eb2a..dcdf19ca1c3603 100644 --- a/homeassistant/components/toon/.translations/ko.json +++ b/homeassistant/components/toon/.translations/ko.json @@ -4,7 +4,7 @@ "client_id": "\ud074\ub77c\uc774\uc5b8\ud2b8 ID \uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "client_secret": "\ud074\ub77c\uc774\uc5b8\ud2b8 \ube44\ubc00\ubc88\ud638\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "no_agreements": "\uc774 \uacc4\uc815\uc5d0\ub294 Toon \ub514\uc2a4\ud50c\ub808\uc774\uac00 \uc5c6\uc2b5\ub2c8\ub2e4.", - "no_app": "Toon \uc744 \uc778\uc99d\ud558\uae30 \uc804\uc5d0 Toon \ub97c \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/toon/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694.", + "no_app": "Toon \uc744 \uc778\uc99d\ud558\ub824\uba74 \uba3c\uc800 Toon \uc744 \uad6c\uc131\ud574\uc57c \ud569\ub2c8\ub2e4. [\uc548\ub0b4](https://www.home-assistant.io/components/toon/) \ub97c \uc77d\uc5b4\ubcf4\uc138\uc694.", "unknown_auth_fail": "\uc778\uc99d\ud558\ub294 \ub3d9\uc548 \uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4." }, "error": { diff --git a/homeassistant/components/toon/.translations/nn.json b/homeassistant/components/toon/.translations/nn.json new file mode 100644 index 00000000000000..b8dbeff27cacce --- /dev/null +++ b/homeassistant/components/toon/.translations/nn.json @@ -0,0 +1,5 @@ +{ + "config": { + "title": "Toon" + } +} \ No newline at end of file diff --git a/homeassistant/components/tradfri/.translations/nn.json b/homeassistant/components/tradfri/.translations/nn.json index b9c68668dacca4..544604e2b2a5cd 100644 --- a/homeassistant/components/tradfri/.translations/nn.json +++ b/homeassistant/components/tradfri/.translations/nn.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "Klarte ikkje \u00e5 kople til gatewayen.", - "invalid_key": "Kunne ikkje registrere med den brukte n\u00f8kkelen. Dersom dette held fram, pr\u00f8v \u00e5 starte gatewayen p\u00e5 nytt. ", + "invalid_key": "Kunne ikkje registrere med den brukte n\u00f8kkelen. Dersom dette held fram, pr\u00f8v \u00e5 starta gatewayen p\u00e5 ny.", "timeout": "Tida gjekk ut for validering av kode" }, "step": { diff --git a/homeassistant/components/upnp/.translations/nn.json b/homeassistant/components/upnp/.translations/nn.json new file mode 100644 index 00000000000000..286efcf0353076 --- /dev/null +++ b/homeassistant/components/upnp/.translations/nn.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "no_sensors_or_port_mapping": "I det minste, aktiver sensor eller portkartlegging" + }, + "error": { + "one": "Ein", + "other": "Andre" + }, + "step": { + "init": { + "title": "UPnP / IGD" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/upnp/.translations/ru.json b/homeassistant/components/upnp/.translations/ru.json index 668b9a377fc18b..8d41ec1d5def31 100644 --- a/homeassistant/components/upnp/.translations/ru.json +++ b/homeassistant/components/upnp/.translations/ru.json @@ -5,7 +5,7 @@ "incomplete_device": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435\u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 UPnP", "no_devices_discovered": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e UPnP / IGD", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 UPnP / IGD \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", - "no_sensors_or_port_mapping": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0438\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432 ", + "no_sensors_or_port_mapping": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0438\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u043e\u0432", "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." }, "step": { diff --git a/homeassistant/components/zwave/.translations/nn.json b/homeassistant/components/zwave/.translations/nn.json new file mode 100644 index 00000000000000..ebd9d44796c7d2 --- /dev/null +++ b/homeassistant/components/zwave/.translations/nn.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Sj\u00e5 [www.home-assistant.io/docs/z-wave/installation/](https://www.home-assistant.io/docs/z-wave/installation/) for informasjon om konfigurasjonsvariablene." + } + } + } +} \ No newline at end of file From 373b2009c934511cb17fd0bb544edc9d81028318 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 10 Apr 2019 15:42:10 -0700 Subject: [PATCH 568/605] Catch connection reset (#22982) --- homeassistant/components/websocket_api/http.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index 85cb256df9009d..b51e5d5699d9e8 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -53,7 +53,8 @@ def __init__(self, hass, request): async def _writer(self): """Write outgoing messages.""" # Exceptions if Socket disconnected or cancelled by connection handler - with suppress(RuntimeError, *CANCELLATION_ERRORS): + with suppress(RuntimeError, ConnectionResetError, + *CANCELLATION_ERRORS): while not self.wsock.closed: message = await self._to_write.get() if message is None: From c2cfc4a8132af744a84a00cd059dc2ca32f48738 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 11 Apr 2019 01:10:14 +0200 Subject: [PATCH 569/605] Stream support for Netatmo cameras (#22952) * Add stream feature * Add a missing slash * Get config parameter * Get default quality --- homeassistant/components/netatmo/camera.py | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netatmo/camera.py b/homeassistant/components/netatmo/camera.py index 513852cde23990..6b80c3061b567e 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -4,7 +4,8 @@ import requests import voluptuous as vol -from homeassistant.components.camera import PLATFORM_SCHEMA, Camera +from homeassistant.components.camera import ( + PLATFORM_SCHEMA, Camera, SUPPORT_STREAM) from homeassistant.const import CONF_VERIFY_SSL from homeassistant.helpers import config_validation as cv @@ -16,12 +17,19 @@ CONF_HOME = 'home' CONF_CAMERAS = 'cameras' +CONF_QUALITY = 'quality' + +DEFAULT_QUALITY = 'high' + +VALID_QUALITIES = ['high', 'medium', 'low', 'poor'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, vol.Optional(CONF_HOME): cv.string, vol.Optional(CONF_CAMERAS, default=[]): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_QUALITY, default=DEFAULT_QUALITY): + vol.All(cv.string, vol.In(VALID_QUALITIES)), }) @@ -30,6 +38,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): netatmo = hass.components.netatmo home = config.get(CONF_HOME) verify_ssl = config.get(CONF_VERIFY_SSL, True) + quality = config.get(CONF_QUALITY, DEFAULT_QUALITY) import pyatmo try: data = CameraData(hass, netatmo.NETATMO_AUTH, home) @@ -40,7 +49,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): camera_name not in config[CONF_CAMERAS]: continue add_entities([NetatmoCamera(data, camera_name, home, - camera_type, verify_ssl)]) + camera_type, verify_ssl, quality)]) data.get_persons() except pyatmo.NoDevice: return None @@ -49,12 +58,14 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class NetatmoCamera(Camera): """Representation of the images published from a Netatmo camera.""" - def __init__(self, data, camera_name, home, camera_type, verify_ssl): + def __init__(self, data, camera_name, home, camera_type, verify_ssl, + quality): """Set up for access to the Netatmo camera images.""" super(NetatmoCamera, self).__init__() self._data = data self._camera_name = camera_name self._verify_ssl = verify_ssl + self._quality = quality if home: self._name = home + ' / ' + camera_name else: @@ -105,3 +116,16 @@ def model(self): if self._cameratype == "NACamera": return "Welcome" return None + + @property + def supported_features(self): + """Return supported features.""" + return SUPPORT_STREAM + + @property + def stream_source(self): + """Return the stream source.""" + url = '{0}/live/files/{1}/index.m3u8' + if self._localurl: + return url.format(self._localurl, self._quality) + return url.format(self._vpnurl, self._quality) From f33bf718c7cd189629f5b877c891769a8d604d6c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 10 Apr 2019 21:35:37 -0700 Subject: [PATCH 570/605] Google Assistant: Migrate light setting trait to use HSV color spectrum (#22980) * Migrate light setting trait to use HSV * Fix tests * Fix all the typos --- .../components/google_assistant/trait.py | 37 +++++++++++++------ .../google_assistant/test_google_assistant.py | 8 +++- .../google_assistant/test_smart_home.py | 32 +++++++++++----- .../components/google_assistant/test_trait.py | 35 +++++++++++++++--- 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 124649982117e8..f554f3375f27ee 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -300,17 +300,19 @@ def sync_attributes(self): response = {} if features & light.SUPPORT_COLOR: - response['colorModel'] = 'rgb' + response['colorModel'] = 'hsv' if features & light.SUPPORT_COLOR_TEMP: # Max Kelvin is Min Mireds K = 1000000 / mireds # Min Kevin is Max Mireds K = 1000000 / mireds - response['temperatureMaxK'] = \ + response['colorTemperatureRange'] = { + 'temperatureMaxK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MIN_MIREDS)) - response['temperatureMinK'] = \ + attrs.get(light.ATTR_MIN_MIREDS)), + 'temperatureMinK': color_util.color_temperature_mired_to_kelvin( - attrs.get(light.ATTR_MAX_MIREDS)) + attrs.get(light.ATTR_MAX_MIREDS)), + } return response @@ -321,12 +323,13 @@ def query_attributes(self): if features & light.SUPPORT_COLOR: color_hs = self.state.attributes.get(light.ATTR_HS_COLOR) + brightness = self.state.attributes.get(light.ATTR_BRIGHTNESS, 1) if color_hs is not None: - color['spectrumRGB'] = int( - color_util.color_rgb_to_hex( - *color_util.color_hs_to_RGB(*color_hs)), - 16 - ) + color['spectrumHsv'] = { + 'hue': color_hs[0], + 'saturation': color_hs[1]/100, + 'value': brightness/255, + } if features & light.SUPPORT_COLOR_TEMP: temp = self.state.attributes.get(light.ATTR_COLOR_TEMP) @@ -335,7 +338,7 @@ def query_attributes(self): _LOGGER.warning('Entity %s has incorrect color temperature %s', self.state.entity_id, temp) elif temp is not None: - color['temperature'] = \ + color['temperatureK'] = \ color_util.color_temperature_mired_to_kelvin(temp) response = {} @@ -377,6 +380,18 @@ async def execute(self, command, data, params): light.ATTR_HS_COLOR: color }, blocking=True, context=data.context) + elif 'spectrumHSV' in params['color']: + color = params['color']['spectrumHSV'] + saturation = color['saturation'] * 100 + brightness = color['value'] * 255 + + await self.hass.services.async_call( + light.DOMAIN, SERVICE_TURN_ON, { + ATTR_ENTITY_ID: self.state.entity_id, + light.ATTR_HS_COLOR: [color['hue'], saturation], + light.ATTR_BRIGHTNESS: brightness + }, blocking=True, context=data.context) + @register_trait class SceneTrait(_Trait): diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 60df4a8e79c8ff..19e1858d4f5cf2 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -174,8 +174,12 @@ def test_query_request(hass_fixture, assistant_client, auth_header): assert devices['light.bed_light']['on'] is False assert devices['light.ceiling_lights']['on'] is True assert devices['light.ceiling_lights']['brightness'] == 70 - assert devices['light.kitchen_lights']['color']['spectrumRGB'] == 16727919 - assert devices['light.kitchen_lights']['color']['temperature'] == 4166 + assert devices['light.kitchen_lights']['color']['spectrumHsv'] == { + 'hue': 345, + 'saturation': 0.75, + 'value': 0.7058823529411765, + } + assert devices['light.kitchen_lights']['color']['temperatureK'] == 4166 assert devices['media_player.lounge_room']['on'] is True diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index d33c1f1bc5b65c..be32d4cb5d9269 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -98,9 +98,11 @@ async def test_sync_message(hass): 'type': sh.TYPE_LIGHT, 'willReportState': False, 'attributes': { - 'colorModel': 'rgb', - 'temperatureMinK': 2000, - 'temperatureMaxK': 6535, + 'colorModel': 'hsv', + 'colorTemperatureRange': { + 'temperatureMinK': 2000, + 'temperatureMaxK': 6535, + } }, 'roomHint': 'Living Room' }] @@ -176,9 +178,11 @@ async def test_sync_in_area(hass, registries): 'type': sh.TYPE_LIGHT, 'willReportState': False, 'attributes': { - 'colorModel': 'rgb', - 'temperatureMinK': 2000, - 'temperatureMaxK': 6535, + 'colorModel': 'hsv', + 'colorTemperatureRange': { + 'temperatureMinK': 2000, + 'temperatureMaxK': 6535, + } }, 'roomHint': 'Living Room' }] @@ -252,8 +256,12 @@ async def test_query_message(hass): 'online': True, 'brightness': 30, 'color': { - 'spectrumRGB': 4194303, - 'temperature': 2500, + 'spectrumHsv': { + 'hue': 180, + 'saturation': 0.75, + 'value': 0.3058823529411765, + }, + 'temperatureK': 2500, } }, } @@ -338,8 +346,12 @@ async def test_execute(hass): "online": True, 'brightness': 20, 'color': { - 'spectrumRGB': 16773155, - 'temperature': 2631, + 'spectrumHsv': { + 'hue': 56, + 'saturation': 0.86, + 'value': 0.2, + }, + 'temperatureK': 2631, }, } }] diff --git a/tests/components/google_assistant/test_trait.py b/tests/components/google_assistant/test_trait.py index 4a84d789068629..9887c1da2cc7aa 100644 --- a/tests/components/google_assistant/test_trait.py +++ b/tests/components/google_assistant/test_trait.py @@ -459,17 +459,22 @@ async def test_color_setting_color_light(hass): light.SUPPORT_COLOR, None) trt = trait.ColorSettingTrait(hass, State('light.bla', STATE_ON, { - light.ATTR_HS_COLOR: (0, 94), + light.ATTR_HS_COLOR: (20, 94), + light.ATTR_BRIGHTNESS: 200, ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR, }), BASIC_CONFIG) assert trt.sync_attributes() == { - 'colorModel': 'rgb' + 'colorModel': 'hsv' } assert trt.query_attributes() == { 'color': { - 'spectrumRGB': 16715535 + 'spectrumHsv': { + 'hue': 20, + 'saturation': 0.94, + 'value': 200 / 255, + } } } @@ -491,6 +496,22 @@ async def test_color_setting_color_light(hass): light.ATTR_HS_COLOR: (240, 93.725), } + await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, BASIC_DATA, { + 'color': { + 'spectrumHSV': { + 'hue': 100, + 'saturation': .50, + 'value': .20, + } + } + }) + assert len(calls) == 2 + assert calls[1].data == { + ATTR_ENTITY_ID: 'light.bla', + light.ATTR_HS_COLOR: [100, 50], + light.ATTR_BRIGHTNESS: .2 * 255, + } + async def test_color_setting_temperature_light(hass): """Test ColorTemperature trait support for light domain.""" @@ -506,13 +527,15 @@ async def test_color_setting_temperature_light(hass): }), BASIC_CONFIG) assert trt.sync_attributes() == { - 'temperatureMinK': 2000, - 'temperatureMaxK': 5000, + 'colorTemperatureRange': { + 'temperatureMinK': 2000, + 'temperatureMaxK': 5000, + } } assert trt.query_attributes() == { 'color': { - 'temperature': 3333 + 'temperatureK': 3333 } } From f5c677146ad306aca163380ecceeea8f4361af4c Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 10 Apr 2019 21:58:50 -0700 Subject: [PATCH 571/605] Make inlined JSON example valid --- homeassistant/components/auth/__init__.py | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/auth/__init__.py b/homeassistant/components/auth/__init__.py index 19edfe5a618a42..d0157158aca6b2 100644 --- a/homeassistant/components/auth/__init__.py +++ b/homeassistant/components/auth/__init__.py @@ -78,20 +78,16 @@ "result": { "id": "USER_ID", "name": "John Doe", - "is_owner': true, - "credentials": [ - { - "auth_provider_type": "homeassistant", - "auth_provider_id": null - } - ], - "mfa_modules": [ - { - "id": "totp", - "name": "TOTP", - "enabled": true, - } - ] + "is_owner": true, + "credentials": [{ + "auth_provider_type": "homeassistant", + "auth_provider_id": null + }], + "mfa_modules": [{ + "id": "totp", + "name": "TOTP", + "enabled": true + }] } } From 8a81286abbc30dd4101d854e2cba0b1a61eb5b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=BDdrale?= Date: Thu, 11 Apr 2019 10:25:46 +0200 Subject: [PATCH 572/605] Bump pyubee version to support more models and detect model automatically (#22450) * Bump pyubee to 0.4, support more models and detect model automatically * Update requirements_all.txt * Check for supported models * Add model aliases * Code clean-up * Updated code to meet reviewer's requests. * Updated code to meet reviewer's requests. * Minor update * Minor update * Populate mac2name dict * Return list of MAC addresses, not dict Co-Authored-By: mzdrale * Minor update --- .../components/ubee/device_tracker.py | 77 +++++++------------ homeassistant/components/ubee/manifest.json | 2 +- requirements_all.txt | 2 +- 3 files changed, 31 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/ubee/device_tracker.py b/homeassistant/components/ubee/device_tracker.py index bbe028bbb78fc4..f73f58f3a1f258 100644 --- a/homeassistant/components/ubee/device_tracker.py +++ b/homeassistant/components/ubee/device_tracker.py @@ -9,79 +9,60 @@ CONF_HOST, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyubee==0.2'] +REQUIREMENTS = ['pyubee==0.5'] _LOGGER = logging.getLogger(__name__) +CONF_MODEL = 'model' +DEFAULT_MODEL = 'detect' + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Required(CONF_USERNAME): cv.string, + vol.Optional(CONF_MODEL, default=DEFAULT_MODEL): cv.string }) def get_scanner(hass, config): """Validate the configuration and return a Ubee scanner.""" - try: - return UbeeDeviceScanner(config[DOMAIN]) - except ConnectionError: + info = config[DOMAIN] + host = info[CONF_HOST] + username = info[CONF_USERNAME] + password = info[CONF_PASSWORD] + model = info[CONF_MODEL] + + from pyubee import Ubee + ubee = Ubee(host, username, password, model) + if not ubee.login(): + _LOGGER.error("Login failed") return None + scanner = UbeeDeviceScanner(ubee) + return scanner + class UbeeDeviceScanner(DeviceScanner): """This class queries a wireless Ubee router.""" - def __init__(self, config): + def __init__(self, ubee): """Initialize the Ubee scanner.""" - from pyubee import Ubee - - self.host = config[CONF_HOST] - self.username = config[CONF_USERNAME] - self.password = config[CONF_PASSWORD] - - self.last_results = {} - self.mac2name = {} - - self.ubee = Ubee(self.host, self.username, self.password) - _LOGGER.info("Logging in") - results = self.get_connected_devices() - self.success_init = results is not None - - if self.success_init: - self.last_results = results - else: - _LOGGER.error("Login failed") + self._ubee = ubee + self._mac2name = {} def scan_devices(self): """Scan for new devices and return a list with found device IDs.""" - self._update_info() - - return self.last_results + devices = self._get_connected_devices() + self._mac2name = devices + return list(devices) def get_device_name(self, device): """Return the name of the given device or None if we don't know.""" - if device in self.mac2name: - return self.mac2name.get(device) - - return None - - def _update_info(self): - """Retrieve latest information from the Ubee router.""" - if not self.success_init: - return - - _LOGGER.debug("Scanning") - results = self.get_connected_devices() - - if results is None: - _LOGGER.warning("Error scanning devices") - return - - self.last_results = results or [] + return self._mac2name.get(device) - def get_connected_devices(self): + def _get_connected_devices(self): """List connected devices with pyubee.""" - if not self.ubee.session_active(): - self.ubee.login() + if not self._ubee.session_active(): + self._ubee.login() - return self.ubee.get_connected_devices() + return self._ubee.get_connected_devices() diff --git a/homeassistant/components/ubee/manifest.json b/homeassistant/components/ubee/manifest.json index c19c72e86862d3..524dcb1d77b7a9 100644 --- a/homeassistant/components/ubee/manifest.json +++ b/homeassistant/components/ubee/manifest.json @@ -3,7 +3,7 @@ "name": "Ubee", "documentation": "https://www.home-assistant.io/components/ubee", "requirements": [ - "pyubee==0.2" + "pyubee==0.5" ], "dependencies": [], "codeowners": [] diff --git a/requirements_all.txt b/requirements_all.txt index 9738d5e0191965..922239c44ef0c2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1423,7 +1423,7 @@ pytradfri[async]==6.0.1 pytrafikverket==0.1.5.9 # homeassistant.components.ubee -pyubee==0.2 +pyubee==0.5 # homeassistant.components.unifi pyunifi==2.16 From 6ba9ccf05294451075e7147a225cf123c77ad34b Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Thu, 11 Apr 2019 01:26:36 -0700 Subject: [PATCH 573/605] Load requirements and dependencies from manifests. Fallback to current `REQUIREMENTS` and `DEPENDENCIES` (#22717) * Load dependencies from manifests. Fallback to current DEPENDENCIES * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Fix tests * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix light tests [skip ci] * Fix tests/common * Fix some mqtt tests [skip ci] * Fix tests and component manifests which have only one platform * Fix rflink tests * Fix more tests and manifests * Readability over shorthand format * Fix demo/notify tests * Load dependencies from manifests. Fallback to current DEPENDENCIES * Load requirements from manifests. Fallback to current REQUIREMENTS * Fix typing * Ignore typing correctly * Split out dependency processing to a new method * Only pull from manifest if dependencies is non empty * Inline temporary function * Fix tests and component manifests which have only one platform * Fix rflink tests * Readability over shorthand format * Clean up requirements * Use integration to resolve deps/reqs * Lint * Lint * revert a change * Revert a test change * Fix types * Fix types * Add back cache for load component * Fix test_component_not_found * Move light.test and device_tracker.test into test package instead with manifest to fix tests * Fix broken device_tracker tests * Add docstrings to __init__ * Fix all of the light tests that I broke earlier * Embed the test.switch platform to fix other tests * Embed and fix the test.imagimage_processing platform * Fix tests for nx584 * Add dependencies from platform file's DEPENDENCIES * Try to setup component when entity_platform is setting up Fix tests in helpers folder * Rewrite test_setup * Simplify * Lint * Disable demo component if running in test Temp workaround to unblock CI tests * Skip demo tests * Fix config entry test * Fix repeat test * Clarify doc * One extra guard * Fix import * Lint * Workaround google tts --- homeassistant/components/arlo/manifest.json | 4 +- homeassistant/components/arwn/manifest.json | 4 +- .../components/asterisk_cdr/manifest.json | 4 +- .../components/automatic/manifest.json | 4 +- .../components/automation/manifest.json | 3 +- homeassistant/components/canary/manifest.json | 4 +- homeassistant/components/demo/__init__.py | 7 +- homeassistant/components/fitbit/manifest.json | 4 +- homeassistant/components/flexit/manifest.json | 4 +- homeassistant/components/flux/manifest.json | 4 +- .../generic_thermostat/manifest.json | 5 +- homeassistant/components/google/__init__.py | 3 + .../components/history_stats/manifest.json | 4 +- .../components/manual_mqtt/manifest.json | 4 +- homeassistant/components/mqtt/manifest.json | 4 +- .../components/mqtt_json/manifest.json | 4 +- .../components/mqtt_room/manifest.json | 4 +- .../components/mystrom/manifest.json | 4 +- .../components/netatmo_public/manifest.json | 4 +- homeassistant/components/netio/manifest.json | 4 +- homeassistant/components/onvif/manifest.json | 4 +- homeassistant/components/ring/manifest.json | 4 +- .../components/spotify/manifest.json | 4 +- .../components/telegram/manifest.json | 4 +- homeassistant/components/tof/manifest.json | 4 +- homeassistant/components/torque/manifest.json | 4 +- .../components/twilio_call/manifest.json | 4 +- .../components/twilio_sms/manifest.json | 4 +- homeassistant/components/xiaomi/manifest.json | 4 +- homeassistant/components/yi/manifest.json | 4 +- homeassistant/config_entries.py | 11 +- homeassistant/loader.py | 29 +++-- homeassistant/setup.py | 83 +++++++++----- requirements_test_all.txt | 10 ++ script/gen_requirements_all.py | 3 + tests/common.py | 23 +++- tests/components/demo/test_geo_location.py | 9 +- tests/components/demo/test_init.py | 3 + .../device_sun_light_trigger/test_init.py | 4 +- tests/components/device_tracker/test_init.py | 10 +- tests/components/flux/test_switch.py | 32 +++--- tests/components/frontend/test_init.py | 4 +- .../generic_thermostat/test_climate.py | 3 +- tests/components/light/test_init.py | 10 +- tests/components/nx584/test_binary_sensor.py | 2 +- tests/components/scene/test_init.py | 2 +- tests/components/switch/test_init.py | 6 +- tests/helpers/test_discovery.py | 10 +- tests/helpers/test_entity_component.py | 44 ++++---- tests/helpers/test_entity_platform.py | 21 ++-- tests/helpers/test_translation.py | 6 +- tests/test_config_entries.py | 33 ++++-- tests/test_loader.py | 28 ++--- tests/test_setup.py | 105 +++++++++--------- tests/testing_config/__init__.py | 1 + .../custom_components/__init__.py | 1 + .../{test_embedded.py => test_legacy.py} | 0 .../.translations/switch.de.json} | 0 .../.translations/switch.en.json} | 0 .../.translations/switch.es.json} | 0 .../custom_components/test/__init__.py | 1 + .../test.py => test/device_tracker.py} | 0 .../test.py => test/image_processing.py} | 0 .../{light/test.py => test/light.py} | 0 .../custom_components/test/manifest.json | 8 ++ .../{switch/test.py => test/switch.py} | 0 66 files changed, 391 insertions(+), 233 deletions(-) create mode 100644 tests/testing_config/__init__.py create mode 100644 tests/testing_config/custom_components/__init__.py rename tests/testing_config/custom_components/switch/{test_embedded.py => test_legacy.py} (100%) rename tests/testing_config/custom_components/{switch/.translations/test.de.json => test/.translations/switch.de.json} (100%) rename tests/testing_config/custom_components/{switch/.translations/test.en.json => test/.translations/switch.en.json} (100%) rename tests/testing_config/custom_components/{switch/.translations/test.es.json => test/.translations/switch.es.json} (100%) create mode 100644 tests/testing_config/custom_components/test/__init__.py rename tests/testing_config/custom_components/{device_tracker/test.py => test/device_tracker.py} (100%) rename tests/testing_config/custom_components/{image_processing/test.py => test/image_processing.py} (100%) rename tests/testing_config/custom_components/{light/test.py => test/light.py} (100%) create mode 100644 tests/testing_config/custom_components/test/manifest.json rename tests/testing_config/custom_components/{switch/test.py => test/switch.py} (100%) diff --git a/homeassistant/components/arlo/manifest.json b/homeassistant/components/arlo/manifest.json index a8b6befb70fa45..35803d0d4f6afb 100644 --- a/homeassistant/components/arlo/manifest.json +++ b/homeassistant/components/arlo/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "pyarlo==0.2.3" ], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [] } diff --git a/homeassistant/components/arwn/manifest.json b/homeassistant/components/arwn/manifest.json index 15ef7fa48ba8fe..1c861aa67e28bf 100644 --- a/homeassistant/components/arwn/manifest.json +++ b/homeassistant/components/arwn/manifest.json @@ -3,6 +3,8 @@ "name": "Arwn", "documentation": "https://www.home-assistant.io/components/arwn", "requirements": [], - "dependencies": [], + "dependencies": [ + "mqtt" + ], "codeowners": [] } diff --git a/homeassistant/components/asterisk_cdr/manifest.json b/homeassistant/components/asterisk_cdr/manifest.json index 2c8713ac191b83..db1308b0483d78 100644 --- a/homeassistant/components/asterisk_cdr/manifest.json +++ b/homeassistant/components/asterisk_cdr/manifest.json @@ -3,6 +3,8 @@ "name": "Asterisk cdr", "documentation": "https://www.home-assistant.io/components/asterisk_cdr", "requirements": [], - "dependencies": [], + "dependencies": [ + "asterisk_mbox" + ], "codeowners": [] } diff --git a/homeassistant/components/automatic/manifest.json b/homeassistant/components/automatic/manifest.json index db2f676813eea5..50bd02d2ac180c 100644 --- a/homeassistant/components/automatic/manifest.json +++ b/homeassistant/components/automatic/manifest.json @@ -5,7 +5,9 @@ "requirements": [ "aioautomatic==0.6.5" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [ "@armills" ] diff --git a/homeassistant/components/automation/manifest.json b/homeassistant/components/automation/manifest.json index 93f1abe0f0d894..ea63d4ff98a31c 100644 --- a/homeassistant/components/automation/manifest.json +++ b/homeassistant/components/automation/manifest.json @@ -4,7 +4,8 @@ "documentation": "https://www.home-assistant.io/components/automation", "requirements": [], "dependencies": [ - "group" + "group", + "webhook" ], "codeowners": [ "@home-assistant/core" diff --git a/homeassistant/components/canary/manifest.json b/homeassistant/components/canary/manifest.json index e7cc5fa7efc68a..346c1c99f6df6b 100644 --- a/homeassistant/components/canary/manifest.json +++ b/homeassistant/components/canary/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "py-canary==0.5.0" ], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [] } diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 70c1341145de8c..354f0c0e37541a 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -1,6 +1,7 @@ """Set up the demo environment that mimics interaction with devices.""" import asyncio import time +import sys from homeassistant import bootstrap import homeassistant.core as ha @@ -31,7 +32,7 @@ ] -async def async_setup(hass, config): +async def _async_setup(hass, config): """Set up the demo environment.""" group = hass.components.group configurator = hass.components.configurator @@ -224,3 +225,7 @@ def setup_configurator(): hass.async_add_job(setup_configurator) return True + + +if 'pytest' not in sys.modules: + async_setup = _async_setup # pylint: disable=invalid-name diff --git a/homeassistant/components/fitbit/manifest.json b/homeassistant/components/fitbit/manifest.json index d1335a1347d4e1..5b02bca4b6f0ed 100644 --- a/homeassistant/components/fitbit/manifest.json +++ b/homeassistant/components/fitbit/manifest.json @@ -5,7 +5,9 @@ "requirements": [ "fitbit==0.3.0" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [ "@robbiet480" ] diff --git a/homeassistant/components/flexit/manifest.json b/homeassistant/components/flexit/manifest.json index 1af86243f8697f..0ee0e81143cd6c 100644 --- a/homeassistant/components/flexit/manifest.json +++ b/homeassistant/components/flexit/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "pyflexit==0.3" ], - "dependencies": [], + "dependencies": [ + "modbus" + ], "codeowners": [] } diff --git a/homeassistant/components/flux/manifest.json b/homeassistant/components/flux/manifest.json index 8c07a70bca6ff8..d4d67edbd353be 100644 --- a/homeassistant/components/flux/manifest.json +++ b/homeassistant/components/flux/manifest.json @@ -3,6 +3,8 @@ "name": "Flux", "documentation": "https://www.home-assistant.io/components/flux", "requirements": [], - "dependencies": [], + "dependencies": [ + "light" + ], "codeowners": [] } diff --git a/homeassistant/components/generic_thermostat/manifest.json b/homeassistant/components/generic_thermostat/manifest.json index 67306b1e7cdae8..41fb04c84566b0 100644 --- a/homeassistant/components/generic_thermostat/manifest.json +++ b/homeassistant/components/generic_thermostat/manifest.json @@ -3,6 +3,9 @@ "name": "Generic thermostat", "documentation": "https://www.home-assistant.io/components/generic_thermostat", "requirements": [], - "dependencies": [], + "dependencies": [ + "sensor", + "switch" + ], "codeowners": [] } diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 37ee5efbd93b81..0216094de9b872 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -153,6 +153,9 @@ def setup(hass, config): hass.data[DATA_INDEX] = {} conf = config.get(DOMAIN, {}) + if not conf: + # component is set up by tts platform + return True token_file = hass.config.path(TOKEN_FILE) if not os.path.isfile(token_file): diff --git a/homeassistant/components/history_stats/manifest.json b/homeassistant/components/history_stats/manifest.json index 8e0c1b249109da..ea0abd87c28c42 100644 --- a/homeassistant/components/history_stats/manifest.json +++ b/homeassistant/components/history_stats/manifest.json @@ -3,6 +3,8 @@ "name": "History stats", "documentation": "https://www.home-assistant.io/components/history_stats", "requirements": [], - "dependencies": [], + "dependencies": [ + "history" + ], "codeowners": [] } diff --git a/homeassistant/components/manual_mqtt/manifest.json b/homeassistant/components/manual_mqtt/manifest.json index cc467ade5c1020..81cd1338450fd9 100644 --- a/homeassistant/components/manual_mqtt/manifest.json +++ b/homeassistant/components/manual_mqtt/manifest.json @@ -3,6 +3,8 @@ "name": "Manual mqtt", "documentation": "https://www.home-assistant.io/components/manual_mqtt", "requirements": [], - "dependencies": [], + "dependencies": [ + "mqtt" + ], "codeowners": [] } diff --git a/homeassistant/components/mqtt/manifest.json b/homeassistant/components/mqtt/manifest.json index deed878711a100..dd4d0323a51c08 100644 --- a/homeassistant/components/mqtt/manifest.json +++ b/homeassistant/components/mqtt/manifest.json @@ -6,7 +6,9 @@ "hbmqtt==0.9.4", "paho-mqtt==1.4.0" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [ "@home-assistant/core" ] diff --git a/homeassistant/components/mqtt_json/manifest.json b/homeassistant/components/mqtt_json/manifest.json index 96a0a187e65c65..a1986b2bf2eee2 100644 --- a/homeassistant/components/mqtt_json/manifest.json +++ b/homeassistant/components/mqtt_json/manifest.json @@ -3,6 +3,8 @@ "name": "Mqtt json", "documentation": "https://www.home-assistant.io/components/mqtt_json", "requirements": [], - "dependencies": [], + "dependencies": [ + "mqtt" + ], "codeowners": [] } diff --git a/homeassistant/components/mqtt_room/manifest.json b/homeassistant/components/mqtt_room/manifest.json index e7b37aec50d430..8fc90b0bcb1837 100644 --- a/homeassistant/components/mqtt_room/manifest.json +++ b/homeassistant/components/mqtt_room/manifest.json @@ -3,6 +3,8 @@ "name": "Mqtt room", "documentation": "https://www.home-assistant.io/components/mqtt_room", "requirements": [], - "dependencies": [], + "dependencies": [ + "mqtt" + ], "codeowners": [] } diff --git a/homeassistant/components/mystrom/manifest.json b/homeassistant/components/mystrom/manifest.json index a3744baccb13a3..0e17f33f72ebd2 100644 --- a/homeassistant/components/mystrom/manifest.json +++ b/homeassistant/components/mystrom/manifest.json @@ -5,7 +5,9 @@ "requirements": [ "python-mystrom==0.5.0" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [ "@fabaff" ] diff --git a/homeassistant/components/netatmo_public/manifest.json b/homeassistant/components/netatmo_public/manifest.json index 4327db3f298d6a..1070f27b33c1d2 100644 --- a/homeassistant/components/netatmo_public/manifest.json +++ b/homeassistant/components/netatmo_public/manifest.json @@ -3,6 +3,8 @@ "name": "Netatmo public", "documentation": "https://www.home-assistant.io/components/netatmo_public", "requirements": [], - "dependencies": [], + "dependencies": [ + "netatmo" + ], "codeowners": [] } diff --git a/homeassistant/components/netio/manifest.json b/homeassistant/components/netio/manifest.json index 75649c66abb586..e3675db04d7305 100644 --- a/homeassistant/components/netio/manifest.json +++ b/homeassistant/components/netio/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "pynetio==0.1.9.1" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [] } diff --git a/homeassistant/components/onvif/manifest.json b/homeassistant/components/onvif/manifest.json index 6d5ad256f165cb..bade9f37022eb5 100644 --- a/homeassistant/components/onvif/manifest.json +++ b/homeassistant/components/onvif/manifest.json @@ -7,6 +7,8 @@ "suds-passworddigest-homeassistant==0.1.2a0.dev0", "suds-py3==1.3.3.0" ], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [] } diff --git a/homeassistant/components/ring/manifest.json b/homeassistant/components/ring/manifest.json index 4d1fc62903553e..9dbedad1ffc136 100644 --- a/homeassistant/components/ring/manifest.json +++ b/homeassistant/components/ring/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "ring_doorbell==0.2.3" ], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [] } diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json index 8c2c72e4d2a157..a371f05629e959 100644 --- a/homeassistant/components/spotify/manifest.json +++ b/homeassistant/components/spotify/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "spotipy-homeassistant==2.4.4.dev1" ], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [] } diff --git a/homeassistant/components/telegram/manifest.json b/homeassistant/components/telegram/manifest.json index 6ace3cd5aa0930..8a6dd7fb369d4c 100644 --- a/homeassistant/components/telegram/manifest.json +++ b/homeassistant/components/telegram/manifest.json @@ -3,6 +3,8 @@ "name": "Telegram", "documentation": "https://www.home-assistant.io/components/telegram", "requirements": [], - "dependencies": [], + "dependencies": [ + "telegram_bot" + ], "codeowners": [] } diff --git a/homeassistant/components/tof/manifest.json b/homeassistant/components/tof/manifest.json index 4e1857379c0349..5f64e661a9a5f4 100644 --- a/homeassistant/components/tof/manifest.json +++ b/homeassistant/components/tof/manifest.json @@ -5,6 +5,8 @@ "requirements": [ "VL53L1X2==0.1.5" ], - "dependencies": [], + "dependencies": [ + "rpi_gpio" + ], "codeowners": [] } diff --git a/homeassistant/components/torque/manifest.json b/homeassistant/components/torque/manifest.json index 3e69cb62e68010..9ce41b59861a69 100644 --- a/homeassistant/components/torque/manifest.json +++ b/homeassistant/components/torque/manifest.json @@ -3,6 +3,8 @@ "name": "Torque", "documentation": "https://www.home-assistant.io/components/torque", "requirements": [], - "dependencies": [], + "dependencies": [ + "http" + ], "codeowners": [] } diff --git a/homeassistant/components/twilio_call/manifest.json b/homeassistant/components/twilio_call/manifest.json index 85545084c7b8c8..b235385396be12 100644 --- a/homeassistant/components/twilio_call/manifest.json +++ b/homeassistant/components/twilio_call/manifest.json @@ -3,7 +3,9 @@ "name": "Twilio call", "documentation": "https://www.home-assistant.io/components/twilio_call", "requirements": [], - "dependencies": [], + "dependencies": [ + "twilio" + ], "codeowners": [ "@robbiet480" ] diff --git a/homeassistant/components/twilio_sms/manifest.json b/homeassistant/components/twilio_sms/manifest.json index 25cee38dbc8871..2174dc275b52cf 100644 --- a/homeassistant/components/twilio_sms/manifest.json +++ b/homeassistant/components/twilio_sms/manifest.json @@ -3,7 +3,9 @@ "name": "Twilio sms", "documentation": "https://www.home-assistant.io/components/twilio_sms", "requirements": [], - "dependencies": [], + "dependencies": [ + "twilio" + ], "codeowners": [ "@robbiet480" ] diff --git a/homeassistant/components/xiaomi/manifest.json b/homeassistant/components/xiaomi/manifest.json index 158a2e9b2fc363..d3587100501f04 100644 --- a/homeassistant/components/xiaomi/manifest.json +++ b/homeassistant/components/xiaomi/manifest.json @@ -3,6 +3,8 @@ "name": "Xiaomi", "documentation": "https://www.home-assistant.io/components/xiaomi", "requirements": [], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [] } diff --git a/homeassistant/components/yi/manifest.json b/homeassistant/components/yi/manifest.json index 0a1a6aabc576db..bb7fbf55cbc205 100644 --- a/homeassistant/components/yi/manifest.json +++ b/homeassistant/components/yi/manifest.json @@ -5,7 +5,9 @@ "requirements": [ "aioftp==0.12.0" ], - "dependencies": [], + "dependencies": [ + "ffmpeg" + ], "codeowners": [ "@bachya" ] diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 571cacbaf8fef2..917579d6cf1df6 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -126,7 +126,7 @@ async def async_step_discovery(info): from typing import Callable, Dict, List, Optional, Set # noqa pylint: disable=unused-import import weakref -from homeassistant import data_entry_flow +from homeassistant import data_entry_flow, loader from homeassistant.core import callback, HomeAssistant from homeassistant.exceptions import HomeAssistantError, ConfigEntryNotReady from homeassistant.setup import async_setup_component, async_process_deps_reqs @@ -688,7 +688,12 @@ async def _async_create_flow(self, handler_key, *, context, data): Handler key is the domain of the component that we want to set up. """ - component = getattr(self.hass.components, handler_key) + integration = await loader.async_get_integration( + self.hass, handler_key) + + if integration is None: + raise data_entry_flow.UnknownHandler + handler = HANDLERS.get(handler_key) if handler is None: @@ -698,7 +703,7 @@ async def _async_create_flow(self, handler_key, *, context, data): # Make sure requirements and dependencies of component are resolved await async_process_deps_reqs( - self.hass, self._hass_config, handler, component) + self.hass, self._hass_config, integration) # Create notification. if source in DISCOVERY_SOURCES: diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 17f0130da4dbdd..0b7495bcb6941b 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -52,11 +52,11 @@ _UNDEF = object() -def manifest_from_legacy_module(module: Any) -> Dict: +def manifest_from_legacy_module(module: ModuleType) -> Dict: """Generate a manifest from a legacy module.""" return { - 'domain': module.DOMAIN, - 'name': module.DOMAIN, + 'domain': module.DOMAIN, # type: ignore + 'name': module.DOMAIN, # type: ignore 'documentation': None, 'requirements': getattr(module, 'REQUIREMENTS', []), 'dependencies': getattr(module, 'DEPENDENCIES', []), @@ -68,10 +68,10 @@ class Integration: """An integration in Home Assistant.""" @classmethod - def resolve_from_root(cls, hass: 'HomeAssistant', root_module: Any, + def resolve_from_root(cls, hass: 'HomeAssistant', root_module: ModuleType, domain: str) -> 'Optional[Integration]': """Resolve an integration from a root module.""" - for base in root_module.__path__: + for base in root_module.__path__: # type: ignore manifest_path = ( pathlib.Path(base) / domain / 'manifest.json' ) @@ -117,15 +117,22 @@ def __init__(self, hass: 'HomeAssistant', pkg_path: str, manifest: Dict): self.dependencies = manifest['dependencies'] # type: List[str] self.requirements = manifest['requirements'] # type: List[str] - def get_component(self) -> Any: + def get_component(self) -> ModuleType: """Return the component.""" - return importlib.import_module(self.pkg_path) + cache = self.hass.data.setdefault(DATA_KEY, {}) + if self.domain not in cache: + cache[self.domain] = importlib.import_module(self.pkg_path) + return cache[self.domain] # type: ignore - def get_platform(self, platform_name: str) -> Any: + def get_platform(self, platform_name: str) -> ModuleType: """Return a platform for an integration.""" - return importlib.import_module( - "{}.{}".format(self.pkg_path, platform_name) - ) + cache = self.hass.data.setdefault(DATA_KEY, {}) + full_name = "{}.{}".format(self.domain, platform_name) + if full_name not in cache: + cache[full_name] = importlib.import_module( + "{}.{}".format(self.pkg_path, platform_name) + ) + return cache[full_name] # type: ignore async def async_get_integration(hass: 'HomeAssistant', domain: str)\ diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 0e294200b5f214..0160279a859bbe 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -100,10 +100,16 @@ def log_error(msg: str, link: bool = True) -> None: _LOGGER.error("Setup failed for %s: %s", domain, msg) async_notify_setup_error(hass, domain, link) - component = loader.get_component(hass, domain) + try: + integration = await loader.async_get_integration(hass, domain) + except loader.IntegrationNotFound: + log_error("Integration not found.", False) + return False - if not component: - log_error("Component not found.", False) + try: + component = integration.get_component() + except ImportError: + log_error("Unable to import component", False) return False # Validate all dependencies exist and there are no circular dependencies @@ -128,7 +134,7 @@ def log_error(msg: str, link: bool = True) -> None: return False try: - await async_process_deps_reqs(hass, config, domain, component) + await async_process_deps_reqs(hass, config, integration) except HomeAssistantError as err: log_error(str(err)) return False @@ -183,13 +189,14 @@ def log_error(msg: str, link: bool = True) -> None: hass.bus.async_fire( EVENT_COMPONENT_LOADED, - {ATTR_COMPONENT: component.DOMAIN} # type: ignore + {ATTR_COMPONENT: component.DOMAIN} # type: ignore ) return True -async def async_prepare_setup_platform(hass: core.HomeAssistant, config: Dict, +async def async_prepare_setup_platform(hass: core.HomeAssistant, + hass_config: Dict, domain: str, platform_name: str) \ -> Optional[ModuleType]: """Load a platform and makes sure dependencies are setup. @@ -202,13 +209,18 @@ async def async_prepare_setup_platform(hass: core.HomeAssistant, config: Dict, def log_error(msg: str) -> None: """Log helper.""" _LOGGER.error("Unable to prepare setup for platform %s: %s", - platform_path, msg) + platform_name, msg) async_notify_setup_error(hass, platform_path) - platform = loader.get_platform(hass, domain, platform_name) + try: + integration = await loader.async_get_integration(hass, platform_name) + except loader.IntegrationNotFound: + log_error("Integration not found") + return None - # Not found - if platform is None: + try: + platform = integration.get_platform(domain) + except ImportError: log_error("Platform not found.") return None @@ -216,9 +228,25 @@ def log_error(msg: str) -> None: if platform_path in hass.config.components: return platform + # Platforms cannot exist on their own, they are part of their integration. + # If the integration is not set up yet, and can be set up, set it up. + if integration.domain not in hass.config.components: + try: + component = integration.get_component() + except ImportError: + log_error("Unable to import the component") + return None + + if (hasattr(component, 'setup') + or hasattr(component, 'async_setup')): + if not await async_setup_component( + hass, integration.domain, hass_config + ): + log_error("Unable to set up component.") + return None + try: - await async_process_deps_reqs( - hass, config, platform_path, platform) + await async_process_deps_reqs(hass, hass_config, integration) except HomeAssistantError as err: log_error(str(err)) return None @@ -227,8 +255,8 @@ def log_error(msg: str) -> None: async def async_process_deps_reqs( - hass: core.HomeAssistant, config: Dict, name: str, - module: ModuleType) -> None: + hass: core.HomeAssistant, config: Dict, + integration: loader.Integration) -> None: """Process all dependencies and requirements for a module. Module is a Python module of either a component or platform. @@ -237,24 +265,23 @@ async def async_process_deps_reqs( if processed is None: processed = hass.data[DATA_DEPS_REQS] = set() - elif name in processed: + elif integration.domain in processed: return - if hasattr(module, 'DEPENDENCIES'): - dep_success = await _async_process_dependencies( - hass, config, name, module.DEPENDENCIES) # type: ignore - - if not dep_success: - raise HomeAssistantError("Could not set up all dependencies.") - - if not hass.config.skip_pip and hasattr(module, 'REQUIREMENTS'): - req_success = await requirements.async_process_requirements( - hass, name, module.REQUIREMENTS) # type: ignore + if integration.dependencies and not await _async_process_dependencies( + hass, + config, + integration.domain, + integration.dependencies + ): + raise HomeAssistantError("Could not set up all dependencies.") - if not req_success: - raise HomeAssistantError("Could not install all requirements.") + if (not hass.config.skip_pip and integration.requirements and + not await requirements.async_process_requirements( + hass, integration.domain, integration.requirements)): + raise HomeAssistantError("Could not install all requirements.") - processed.add(name) + processed.add(integration.domain) @core.callback diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 12c2f0e51ad0cf..c69d4026227937 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -111,6 +111,9 @@ geojson_client==0.3 # homeassistant.components.geo_rss_events georss_generic_client==0.2 +# homeassistant.components.google +google-api-python-client==1.6.4 + # homeassistant.components.ffmpeg ha-ffmpeg==2.0 @@ -138,6 +141,10 @@ homekit[IP]==0.13.0 # homeassistant.components.homematicip_cloud homematicip==0.10.6 +# homeassistant.components.google +# homeassistant.components.remember_the_milk +httplib2==0.10.3 + # homeassistant.components.influxdb influxdb==5.2.0 @@ -165,6 +172,9 @@ mficlient==0.3.0 # homeassistant.components.trend numpy==1.16.2 +# homeassistant.components.google +oauth2client==4.0.0 + # homeassistant.components.mqtt # homeassistant.components.shiftr paho-mqtt==1.4.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 8f6172c2323a64..e1179c904ce285 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -62,6 +62,7 @@ 'foobot_async', 'geojson_client', 'georss_generic_client', + 'google-api-python-client', 'gTTS-token', 'ha-ffmpeg', 'hangups', @@ -74,6 +75,7 @@ 'home-assistant-frontend', 'homekit[IP]', 'homematicip', + 'httplib2', 'influxdb', 'jsonpath', 'libpurecool', @@ -82,6 +84,7 @@ 'mbddns', 'mficlient', 'numpy', + 'oauth2client', 'paho-mqtt', 'pexpect', 'pilight', diff --git a/tests/common.py b/tests/common.py index 962e98b24e705d..4aa13fc9be6ece 100644 --- a/tests/common.py +++ b/tests/common.py @@ -906,11 +906,26 @@ def mock_integration(hass, module): integration = loader.Integration( hass, 'homeassisant.components.{}'.format(module.DOMAIN), loader.manifest_from_legacy_module(module)) - integration.get_component = lambda: module - - # Backwards compat - loader.set_component(hass, module.DOMAIN, module) + _LOGGER.info("Adding mock integration: %s", module.DOMAIN) hass.data.setdefault( loader.DATA_INTEGRATIONS, {} )[module.DOMAIN] = integration + hass.data.setdefault(loader.DATA_KEY, {})[module.DOMAIN] = module + + +def mock_entity_platform(hass, platform_path, module): + """Mock a entity platform. + + platform_path is in form light.hue. Will create platform + hue.light. + """ + domain, platform_name = platform_path.split('.') + integration_cache = hass.data.setdefault(loader.DATA_KEY, {}) + module_cache = hass.data.setdefault(loader.DATA_KEY, {}) + + if platform_name not in integration_cache: + mock_integration(hass, MockModule(platform_name)) + + _LOGGER.info("Adding mock integration platform: %s", platform_path) + module_cache["{}.{}".format(platform_name, domain)] = module diff --git a/tests/components/demo/test_geo_location.py b/tests/components/demo/test_geo_location.py index c4d01b812f8597..acfc97b00e68f7 100644 --- a/tests/components/demo/test_geo_location.py +++ b/tests/components/demo/test_geo_location.py @@ -40,14 +40,13 @@ def test_setup_platform(self): assert setup_component(self.hass, geo_location.DOMAIN, CONFIG) self.hass.block_till_done() - # In this test, five geolocation entities have been + # In this test, one zone and geolocation entities have been # generated. all_states = self.hass.states.all() - print(all_states) - assert len(all_states) == NUMBER_OF_DEMO_DEVICES + assert len(all_states) == NUMBER_OF_DEMO_DEVICES + 1 # Check a single device's attributes. - state_first_entry = all_states[0] + state_first_entry = all_states[1] # 0 is zone assert abs( state_first_entry.attributes['latitude'] - self.hass.config.latitude @@ -64,5 +63,5 @@ def test_setup_platform(self): # Get all states again, ensure that the number of states is still # the same, but the lists are different. all_states_updated = self.hass.states.all() - assert len(all_states_updated) == NUMBER_OF_DEMO_DEVICES + assert len(all_states_updated) == NUMBER_OF_DEMO_DEVICES + 1 assert all_states != all_states_updated diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index b0b2524180fea2..8ade4f4c278d5a 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -38,6 +38,7 @@ def demo_cleanup(hass): pass +@pytest.mark.skip @asyncio.coroutine def test_if_demo_state_shows_by_default(hass, minimize_demo_platforms): """Test if demo state shows if we give no configuration.""" @@ -46,6 +47,7 @@ def test_if_demo_state_shows_by_default(hass, minimize_demo_platforms): assert hass.states.get('a.Demo_Mode') is not None +@pytest.mark.skip @asyncio.coroutine def test_hiding_demo_state(hass, minimize_demo_platforms): """Test if you can hide the demo card.""" @@ -55,6 +57,7 @@ def test_hiding_demo_state(hass, minimize_demo_platforms): assert hass.states.get('a.Demo_Mode') is None +@pytest.mark.skip @asyncio.coroutine def test_all_entities_can_be_loaded_over_json(hass): """Test if you can hide the demo card.""" diff --git a/tests/components/device_sun_light_trigger/test_init.py b/tests/components/device_sun_light_trigger/test_init.py index f1ef6aa5dd03c9..8c4417f5b291c0 100644 --- a/tests/components/device_sun_light_trigger/test_init.py +++ b/tests/components/device_sun_light_trigger/test_init.py @@ -19,12 +19,12 @@ def scanner(hass): """Initialize components.""" scanner = loader.get_component( - hass, 'device_tracker.test').get_scanner(None, None) + hass, 'test.device_tracker').get_scanner(None, None) scanner.reset() scanner.come_home('DEV1') - loader.get_component(hass, 'light.test').init() + loader.get_component(hass, 'test.light').init() with patch( 'homeassistant.components.device_tracker.load_yaml_config_file', diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 63f1c60327a459..a7c5a1c39033f0 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -192,7 +192,7 @@ async def test_discover_platform(mock_demo_setup_scanner, mock_see, hass): async def test_update_stale(hass): """Test stalled update.""" - scanner = get_component(hass, 'device_tracker.test').SCANNER + scanner = get_component(hass, 'test.device_tracker').SCANNER scanner.reset() scanner.come_home('DEV1') @@ -256,7 +256,7 @@ async def test_device_hidden(hass, yaml_devices): hide_if_away=True) device_tracker.update_config(yaml_devices, dev_id, device) - scanner = get_component(hass, 'device_tracker.test').SCANNER + scanner = get_component(hass, 'test.device_tracker').SCANNER scanner.reset() with assert_setup_component(1, device_tracker.DOMAIN): @@ -275,7 +275,7 @@ async def test_group_all_devices(hass, yaml_devices): hide_if_away=True) device_tracker.update_config(yaml_devices, dev_id, device) - scanner = get_component(hass, 'device_tracker.test').SCANNER + scanner = get_component(hass, 'test.device_tracker').SCANNER scanner.reset() with assert_setup_component(1, device_tracker.DOMAIN): @@ -441,7 +441,7 @@ async def test_see_passive_zone_state(hass): 'zone': zone_info }) - scanner = get_component(hass, 'device_tracker.test').SCANNER + scanner = get_component(hass, 'test.device_tracker').SCANNER scanner.reset() scanner.come_home('dev1') @@ -557,7 +557,7 @@ def test_bad_platform(hass): async def test_adding_unknown_device_to_config(mock_device_tracker_conf, hass): """Test the adding of unknown devices to configuration file.""" - scanner = get_component(hass, 'device_tracker.test').SCANNER + scanner = get_component(hass, 'test.device_tracker').SCANNER scanner.reset() scanner.come_home('DEV1') diff --git a/tests/components/flux/test_switch.py b/tests/components/flux/test_switch.py index c43f1071e339e1..317e20f1457618 100644 --- a/tests/components/flux/test_switch.py +++ b/tests/components/flux/test_switch.py @@ -74,7 +74,7 @@ def test_invalid_config_no_lights(self): def test_flux_when_switch_is_off(self): """Test the flux switch when it is off.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -114,7 +114,7 @@ def event_date(hass, event, now=None): def test_flux_before_sunrise(self): """Test the flux switch before sunrise.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -159,7 +159,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_after_sunrise_before_sunset(self): """Test the flux switch after sunrise and before sunset.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -205,7 +205,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_after_sunset_before_stop(self): """Test the flux switch after sunset and before stop.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -252,7 +252,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_after_stop_before_sunrise(self): """Test the flux switch after stop and before sunrise.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -297,7 +297,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_with_custom_start_stop_times(self): """Test the flux with custom start and stop times.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -347,7 +347,7 @@ def test_flux_before_sunrise_stop_next_day(self): This test has the stop_time on the next day (after midnight). """ - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -398,7 +398,7 @@ def test_flux_after_sunrise_before_sunset_stop_next_day(self): This test has the stop_time on the next day (after midnight). """ - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -448,7 +448,7 @@ def test_flux_after_sunset_before_midnight_stop_next_day(self): This test has the stop_time on the next day (after midnight). """ - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -497,7 +497,7 @@ def test_flux_after_sunset_after_midnight_stop_next_day(self): This test has the stop_time on the next day (after midnight). """ - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -547,7 +547,7 @@ def test_flux_after_stop_before_sunrise_stop_next_day(self): This test has the stop_time on the next day (after midnight). """ - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -594,7 +594,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_with_custom_colortemps(self): """Test the flux with custom start and stop colortemps.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -643,7 +643,7 @@ def event_date(hass, event, now=None): # pylint: disable=invalid-name def test_flux_with_custom_brightness(self): """Test the flux with custom start and stop colortemps.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -690,7 +690,7 @@ def event_date(hass, event, now=None): def test_flux_with_multiple_lights(self): """Test the flux switch with multiple light entities.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -758,7 +758,7 @@ def event_date(hass, event, now=None): def test_flux_with_mired(self): """Test the flux switch´s mode mired.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) @@ -802,7 +802,7 @@ def event_date(hass, event, now=None): def test_flux_with_rgb(self): """Test the flux switch´s mode rgb.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: 'test'}}) diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index e4ed2c15ecb699..497dc25230e8f1 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -78,9 +78,9 @@ def test_frontend_and_static(mock_http_client, mock_onboarded): # Test we can retrieve frontend.js frontendjs = re.search( - r'(?P\/frontend_es5\/app-[A-Za-z0-9]{8}.js)', text) + r'(?P\/frontend_es5\/app.[A-Za-z0-9]{8}.js)', text) - assert frontendjs is not None + assert frontendjs is not None, text resp = yield from mock_http_client.get(frontendjs.groups(0)[0]) assert resp.status == 200 assert 'public' in resp.headers.get('cache-control') diff --git a/tests/components/generic_thermostat/test_climate.py b/tests/components/generic_thermostat/test_climate.py index 49d49fdd3d404b..2d9a5effc2c305 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -98,7 +98,7 @@ async def test_heater_input_boolean(hass, setup_comp_1): async def test_heater_switch(hass, setup_comp_1): """Test heater switching test switch.""" - platform = loader.get_component(hass, 'switch.test') + platform = loader.get_component(hass, 'test.switch') platform.init() switch_1 = platform.DEVICES[1] assert await async_setup_component(hass, switch.DOMAIN, {'switch': { @@ -112,6 +112,7 @@ async def test_heater_switch(hass, setup_comp_1): 'target_sensor': ENT_SENSOR }}) + await hass.async_block_till_done() assert STATE_OFF == \ hass.states.get(heater_switch).state diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 90f2651080c955..c910c2a49a15ce 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -121,7 +121,7 @@ def test_methods(self): def test_services(self): """Test the provided services.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() assert setup_component(self.hass, light.DOMAIN, @@ -308,7 +308,7 @@ def test_services(self): def test_broken_light_profiles(self): """Test light profiles.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE) @@ -323,7 +323,7 @@ def test_broken_light_profiles(self): def test_light_profiles(self): """Test light profiles.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE) @@ -362,7 +362,7 @@ def test_light_profiles(self): def test_default_profiles_group(self): """Test default turn-on light profile for all lights.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE) @@ -400,7 +400,7 @@ def _mock_open(path, *args, **kwargs): def test_default_profiles_light(self): """Test default turn-on light profile for a specific light.""" - platform = loader.get_component(self.hass, 'light.test') + platform = loader.get_component(self.hass, 'test.light') platform.init() user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE) diff --git a/tests/components/nx584/test_binary_sensor.py b/tests/components/nx584/test_binary_sensor.py index 53516885f306e1..ae7b70e7fe6a7e 100644 --- a/tests/components/nx584/test_binary_sensor.py +++ b/tests/components/nx584/test_binary_sensor.py @@ -85,7 +85,7 @@ def test_setup_full_config(self, mock_nx, mock_watcher): def _test_assert_graceful_fail(self, config): """Test the failing.""" assert not setup_component( - self.hass, 'binary_sensor.nx584', config) + self.hass, 'nx584', config) def test_setup_bad_config(self): """Test the setup with bad configuration.""" diff --git a/tests/components/scene/test_init.py b/tests/components/scene/test_init.py index df96b19f3510b9..804344ccb34696 100644 --- a/tests/components/scene/test_init.py +++ b/tests/components/scene/test_init.py @@ -18,7 +18,7 @@ class TestScene(unittest.TestCase): def setUp(self): # pylint: disable=invalid-name """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() - test_light = loader.get_component(self.hass, 'light.test') + test_light = loader.get_component(self.hass, 'test.light') test_light.init() assert setup_component(self.hass, light.DOMAIN, { diff --git a/tests/components/switch/test_init.py b/tests/components/switch/test_init.py index d39c5a24ddc53a..c76278f9b2259c 100644 --- a/tests/components/switch/test_init.py +++ b/tests/components/switch/test_init.py @@ -18,7 +18,7 @@ class TestSwitch(unittest.TestCase): def setUp(self): """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() - platform = loader.get_component(self.hass, 'switch.test') + platform = loader.get_component(self.hass, 'test.switch') platform.init() # Switch 1 is ON, switch 2 is OFF self.switch_1, self.switch_2, self.switch_3 = \ @@ -77,7 +77,7 @@ def test_methods(self): def test_setup_two_platforms(self): """Test with bad configuration.""" # Test if switch component returns 0 switches - test_platform = loader.get_component(self.hass, 'switch.test') + test_platform = loader.get_component(self.hass, 'test.switch') test_platform.init(True) loader.set_component(self.hass, 'switch.test2', test_platform) @@ -99,6 +99,8 @@ async def test_switch_context(hass, hass_admin_user): } }) + await hass.async_block_till_done() + state = hass.states.get('switch.ac') assert state is not None diff --git a/tests/helpers/test_discovery.py b/tests/helpers/test_discovery.py index ffafd3ca146ff4..8bd5e482da40a5 100644 --- a/tests/helpers/test_discovery.py +++ b/tests/helpers/test_discovery.py @@ -132,10 +132,14 @@ def setup_platform(hass, config, add_entities_callback, self.hass, 'test_component', MockModule('test_component', setup=component_setup)) + # dependencies are only set in component level + # since we are using manifest to hold them loader.set_component( - self.hass, 'switch.test_circular', - MockPlatform(setup_platform, - dependencies=['test_component'])) + self.hass, 'test_circular', + MockModule('test_circular', dependencies=['test_component'])) + loader.set_component( + self.hass, 'test_circular.switch', + MockPlatform(setup_platform)) setup.setup_component(self.hass, 'test_component', { 'test_component': None, diff --git a/tests/helpers/test_entity_component.py b/tests/helpers/test_entity_component.py index 6da3293d597b5c..790b7d638e4db9 100644 --- a/tests/helpers/test_entity_component.py +++ b/tests/helpers/test_entity_component.py @@ -21,7 +21,8 @@ from tests.common import ( get_test_home_assistant, MockPlatform, MockModule, mock_coro, - async_fire_time_changed, MockEntity, MockConfigEntry) + async_fire_time_changed, MockEntity, MockConfigEntry, + mock_entity_platform, mock_integration) _LOGGER = logging.getLogger(__name__) DOMAIN = "test_domain" @@ -74,11 +75,14 @@ def test_setup_loads_platforms(self): """Test the loading of the platforms.""" component_setup = Mock(return_value=True) platform_setup = Mock(return_value=None) - loader.set_component( - self.hass, 'test_component', - MockModule('test_component', setup=component_setup)) - loader.set_component(self.hass, 'test_domain.mod2', - MockPlatform(platform_setup, ['test_component'])) + + mock_integration(self.hass, + MockModule('test_component', setup=component_setup)) + # mock the dependencies + mock_integration(self.hass, + MockModule('mod2', dependencies=['test_component'])) + mock_entity_platform(self.hass, 'test_domain.mod2', + MockPlatform(platform_setup)) component = EntityComponent(_LOGGER, DOMAIN, self.hass) @@ -100,9 +104,9 @@ def test_setup_recovers_when_setup_raises(self): platform1_setup = Mock(side_effect=Exception('Broken')) platform2_setup = Mock(return_value=None) - loader.set_component(self.hass, 'test_domain.mod1', + mock_entity_platform(self.hass, 'test_domain.mod1', MockPlatform(platform1_setup)) - loader.set_component(self.hass, 'test_domain.mod2', + mock_entity_platform(self.hass, 'test_domain.mod2', MockPlatform(platform2_setup)) component = EntityComponent(_LOGGER, DOMAIN, self.hass) @@ -147,7 +151,7 @@ def platform_setup(hass, config, add_entities, discovery_info=None): """Test the platform setup.""" add_entities([MockEntity(should_poll=True)]) - loader.set_component(self.hass, 'test_domain.platform', + mock_entity_platform(self.hass, 'test_domain.platform', MockPlatform(platform_setup)) component = EntityComponent(_LOGGER, DOMAIN, self.hass) @@ -174,7 +178,7 @@ def platform_setup(hass, config, add_entities, discovery_info=None): platform = MockPlatform(platform_setup) - loader.set_component(self.hass, 'test_domain.platform', platform) + mock_entity_platform(self.hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, self.hass) @@ -222,7 +226,9 @@ def test_platform_not_ready(hass): """Test that we retry when platform not ready.""" platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady, None]) - loader.set_component(hass, 'test_domain.mod1', + loader.set_component(hass, 'mod1', + MockModule('mod1')) + loader.set_component(hass, 'mod1.test_domain', MockPlatform(platform1_setup)) component = EntityComponent(_LOGGER, DOMAIN, hass) @@ -320,17 +326,15 @@ def test_setup_dependencies_platform(hass): We're explictely testing that we process dependencies even if a component with the same name has already been loaded. """ - loader.set_component(hass, 'test_component', MockModule('test_component')) + loader.set_component(hass, 'test_component', + MockModule('test_component', + dependencies=['test_component2'])) loader.set_component(hass, 'test_component2', MockModule('test_component2')) - loader.set_component( - hass, 'test_component.test_domain', - MockPlatform(dependencies=['test_component', 'test_component2'])) + loader.set_component(hass, 'test_component.test_domain', MockPlatform()) component = EntityComponent(_LOGGER, DOMAIN, hass) - yield from async_setup_component(hass, 'test_component', {}) - yield from component.async_setup({ DOMAIN: { 'platform': 'test_component', @@ -345,7 +349,7 @@ def test_setup_dependencies_platform(hass): async def test_setup_entry(hass): """Test setup entry calls async_setup_entry on platform.""" mock_setup_entry = Mock(return_value=mock_coro(True)) - loader.set_component( + mock_entity_platform( hass, 'test_domain.entry_domain', MockPlatform(async_setup_entry=mock_setup_entry, scan_interval=timedelta(seconds=5))) @@ -374,7 +378,7 @@ async def test_setup_entry_platform_not_exist(hass): async def test_setup_entry_fails_duplicate(hass): """Test we don't allow setting up a config entry twice.""" mock_setup_entry = Mock(return_value=mock_coro(True)) - loader.set_component( + mock_entity_platform( hass, 'test_domain.entry_domain', MockPlatform(async_setup_entry=mock_setup_entry)) @@ -390,7 +394,7 @@ async def test_setup_entry_fails_duplicate(hass): async def test_unload_entry_resets_platform(hass): """Test unloading an entry removes all entities.""" mock_setup_entry = Mock(return_value=mock_coro(True)) - loader.set_component( + mock_entity_platform( hass, 'test_domain.entry_domain', MockPlatform(async_setup_entry=mock_setup_entry)) diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index 6cf0bb0eeeb89c..0fed09b7cbc9b3 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -8,7 +8,6 @@ import pytest from homeassistant.exceptions import PlatformNotReady -import homeassistant.loader as loader from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.entity_component import ( EntityComponent, DEFAULT_SCAN_INTERVAL) @@ -18,7 +17,7 @@ from tests.common import ( get_test_home_assistant, MockPlatform, fire_time_changed, mock_registry, - MockEntity, MockEntityPlatform, MockConfigEntry) + MockEntity, MockEntityPlatform, MockConfigEntry, mock_entity_platform) _LOGGER = logging.getLogger(__name__) DOMAIN = "test_domain" @@ -149,7 +148,7 @@ def platform_setup(hass, config, add_entities, discovery_info=None): platform = MockPlatform(platform_setup) platform.SCAN_INTERVAL = timedelta(seconds=30) - loader.set_component(self.hass, 'test_domain.platform', platform) + mock_entity_platform(self.hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, self.hass) @@ -186,7 +185,7 @@ def test_platform_warn_slow_setup(hass): """Warn we log when platform setup takes a long time.""" platform = MockPlatform() - loader.set_component(hass, 'test_domain.platform', platform) + mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) @@ -199,7 +198,9 @@ def test_platform_warn_slow_setup(hass): }) assert mock_call.called - timeout, logger_method = mock_call.mock_calls[0][1][:2] + # mock_calls[0] is the warning message for component setup + # mock_calls[3] is the warning message for platform setup + timeout, logger_method = mock_call.mock_calls[3][1][:2] assert timeout == entity_platform.SLOW_SETUP_WARNING assert logger_method == _LOGGER.warning @@ -220,7 +221,7 @@ def setup_platform(*args): platform = MockPlatform(async_setup_platform=setup_platform) component = EntityComponent(_LOGGER, DOMAIN, hass) - loader.set_component(hass, 'test_domain.test_platform', platform) + mock_entity_platform(hass, 'test_domain.test_platform', platform) yield from component.async_setup({ DOMAIN: { 'platform': 'test_platform', @@ -255,7 +256,7 @@ async def test_parallel_updates_async_platform(hass): """Test async platform does not have parallel_updates limit by default.""" platform = MockPlatform() - loader.set_component(hass, 'test_domain.platform', platform) + mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} @@ -285,7 +286,7 @@ async def test_parallel_updates_async_platform_with_constant(hass): platform = MockPlatform() platform.PARALLEL_UPDATES = 2 - loader.set_component(hass, 'test_domain.platform', platform) + mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} @@ -316,7 +317,7 @@ async def test_parallel_updates_sync_platform(hass): """Test sync platform parallel_updates default set to 1.""" platform = MockPlatform() - loader.set_component(hass, 'test_domain.platform', platform) + mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} @@ -347,7 +348,7 @@ async def test_parallel_updates_sync_platform_with_constant(hass): platform = MockPlatform() platform.PARALLEL_UPDATES = 2 - loader.set_component(hass, 'test_domain.platform', platform) + mock_entity_platform(hass, 'test_domain.platform', platform) component = EntityComponent(_LOGGER, DOMAIN, hass) component._platforms = {} diff --git a/tests/helpers/test_translation.py b/tests/helpers/test_translation.py index 9d62f204dcd022..34d929b285af94 100644 --- a/tests/helpers/test_translation.py +++ b/tests/helpers/test_translation.py @@ -54,7 +54,7 @@ async def test_component_translation_file(hass): assert path.normpath(translation.component_translation_file( hass, 'switch.test', 'en')) == path.normpath(hass.config.path( - 'custom_components', 'switch', '.translations', 'test.en.json')) + 'custom_components', 'test', '.translations', 'switch.en.json')) assert path.normpath(translation.component_translation_file( hass, 'switch.test_embedded', 'en')) == path.normpath(hass.config.path( @@ -74,9 +74,9 @@ def test_load_translations_files(hass): """Test the load translation files function.""" # Test one valid and one invalid file file1 = hass.config.path( - 'custom_components', 'switch', '.translations', 'test.en.json') + 'custom_components', 'test', '.translations', 'switch.en.json') file2 = hass.config.path( - 'custom_components', 'switch', '.translations', 'invalid.json') + 'custom_components', 'test', '.translations', 'invalid.json') assert translation.load_translations_files({ 'switch.test': file1, 'invalid': file2 diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index fc8541a096a486..cd3f9fd1a895e6 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -13,15 +13,22 @@ from tests.common import ( MockModule, mock_coro, MockConfigEntry, async_fire_time_changed, - MockPlatform, MockEntity) + MockPlatform, MockEntity, mock_integration, mock_entity_platform) -@config_entries.HANDLERS.register('test') -@config_entries.HANDLERS.register('comp') -class MockFlowHandler(config_entries.ConfigFlow): - """Define a mock flow handler.""" +@pytest.fixture(autouse=True) +def mock_handlers(): + """Mock config flows.""" + class MockFlowHandler(config_entries.ConfigFlow): + """Define a mock flow handler.""" - VERSION = 1 + VERSION = 1 + + with patch.dict(config_entries.HANDLERS, { + 'comp': MockFlowHandler, + 'test': MockFlowHandler, + }): + yield @pytest.fixture @@ -185,23 +192,27 @@ async def mock_setup_entry_platform(hass, entry, async_add_entities): """Mock setting up platform.""" async_add_entities([entity]) - loader.set_component(hass, 'test', MockModule( + mock_integration(hass, MockModule( 'test', async_setup_entry=mock_setup_entry, async_unload_entry=mock_unload_entry, async_remove_entry=mock_remove_entry )) - loader.set_component( - hass, 'test.light', + mock_entity_platform( + hass, 'light.test', MockPlatform(async_setup_entry=mock_setup_entry_platform)) - MockConfigEntry(domain='test', entry_id='test1').add_to_manager(manager) + MockConfigEntry( + domain='test_other', entry_id='test1' + ).add_to_manager(manager) entry = MockConfigEntry( domain='test', entry_id='test2', ) entry.add_to_manager(manager) - MockConfigEntry(domain='test', entry_id='test3').add_to_manager(manager) + MockConfigEntry( + domain='test_other', entry_id='test3' + ).add_to_manager(manager) # Check all config entries exist assert [item.entry_id for item in manager.async_entries()] == \ diff --git a/tests/test_loader.py b/tests/test_loader.py index e3888412f0c649..9598906a82ba17 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -1,6 +1,4 @@ """Test to verify that we can load components.""" -import asyncio - import pytest import homeassistant.loader as loader @@ -63,20 +61,18 @@ def test_component_loader_non_existing(hass): components.non_existing -@asyncio.coroutine -def test_component_wrapper(hass): +async def test_component_wrapper(hass): """Test component wrapper.""" calls = async_mock_service(hass, 'persistent_notification', 'create') components = loader.Components(hass) components.persistent_notification.async_create('message') - yield from hass.async_block_till_done() + await hass.async_block_till_done() assert len(calls) == 1 -@asyncio.coroutine -def test_helpers_wrapper(hass): +async def test_helpers_wrapper(hass): """Test helpers wrapper.""" helpers = loader.Helpers(hass) @@ -88,8 +84,8 @@ def discovery_callback(service, discovered): helpers.discovery.async_listen('service_name', discovery_callback) - yield from helpers.discovery.async_discover('service_name', 'hello') - yield from hass.async_block_till_done() + await helpers.discovery.async_discover('service_name', 'hello') + await hass.async_block_till_done() assert result == ['hello'] @@ -104,9 +100,9 @@ async def test_custom_component_name(hass): assert comp.__name__ == 'custom_components.test_package' assert comp.__package__ == 'custom_components.test_package' - comp = loader.get_component(hass, 'light.test') - assert comp.__name__ == 'custom_components.light.test' - assert comp.__package__ == 'custom_components.light' + comp = loader.get_component(hass, 'test.light') + assert comp.__name__ == 'custom_components.test.light' + assert comp.__package__ == 'custom_components.test' # Test custom components is mounted from custom_components.test_package import TEST @@ -119,8 +115,8 @@ async def test_log_warning_custom_component(hass, caplog): assert \ 'You are using a custom component for test_standalone' in caplog.text - loader.get_component(hass, 'light.test') - assert 'You are using a custom component for light.test' in caplog.text + loader.get_component(hass, 'test.light') + assert 'You are using a custom component for test.light' in caplog.text async def test_get_platform(hass, caplog): @@ -132,8 +128,8 @@ async def test_get_platform(hass, caplog): caplog.clear() - legacy_platform = loader.get_platform(hass, 'switch', 'test') - assert legacy_platform.__name__ == 'custom_components.switch.test' + legacy_platform = loader.get_platform(hass, 'switch', 'test_legacy') + assert legacy_platform.__name__ == 'custom_components.switch.test_legacy' assert 'Integrations need to be in their own folder.' in caplog.text diff --git a/tests/test_setup.py b/tests/test_setup.py index 00518776b52aa4..32c431d1d6ac1d 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -20,7 +20,8 @@ from tests.common import \ get_test_home_assistant, MockModule, MockPlatform, \ - assert_setup_component, get_test_config_dir, mock_integration + assert_setup_component, get_test_config_dir, mock_integration, \ + mock_entity_platform ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE VERSION_PATH = os.path.join(get_test_config_dir(), config_util.VERSION_FILE) @@ -50,9 +51,9 @@ def test_validate_component_config(self): 'hello': str } }, required=True) - loader.set_component( + mock_integration( self.hass, - 'comp_conf', MockModule('comp_conf', config_schema=config_schema)) + MockModule('comp_conf', config_schema=config_schema)) with assert_setup_component(0): assert not setup.setup_component(self.hass, 'comp_conf', {}) @@ -97,17 +98,15 @@ def test_validate_platform_config(self, caplog): }) platform_schema_base = PLATFORM_SCHEMA_BASE.extend({ }) - loader.set_component( + mock_integration( self.hass, - 'platform_conf', MockModule('platform_conf', - platform_schema_base=platform_schema_base)) - - loader.set_component( + platform_schema_base=platform_schema_base), + ) + mock_entity_platform( self.hass, 'platform_conf.whatever', - MockPlatform('whatever', - platform_schema=platform_schema)) + MockPlatform(platform_schema=platform_schema)) with assert_setup_component(1): assert setup.setup_component(self.hass, 'platform_conf', { @@ -195,14 +194,13 @@ def test_validate_platform_config_2(self, caplog): platform_schema_base = PLATFORM_SCHEMA_BASE.extend({ 'hello': 'world', }) - loader.set_component( + mock_integration( self.hass, - 'platform_conf', MockModule('platform_conf', platform_schema=platform_schema, platform_schema_base=platform_schema_base)) - loader.set_component( + mock_entity_platform( self.hass, 'platform_conf.whatever', MockPlatform('whatever', @@ -249,13 +247,12 @@ def test_validate_platform_config_3(self, caplog): 'cheers': str, 'hello': 'world', }) - loader.set_component( + mock_integration( self.hass, - 'platform_conf', MockModule('platform_conf', platform_schema=component_schema)) - loader.set_component( + mock_entity_platform( self.hass, 'platform_conf.whatever', MockPlatform('whatever', @@ -296,17 +293,15 @@ def test_validate_platform_config_4(self): """Test entity_namespace in PLATFORM_SCHEMA.""" component_schema = PLATFORM_SCHEMA_BASE platform_schema = PLATFORM_SCHEMA - loader.set_component( + mock_integration( self.hass, - 'platform_conf', MockModule('platform_conf', platform_schema_base=component_schema)) - loader.set_component( + mock_entity_platform( self.hass, 'platform_conf.whatever', - MockPlatform('whatever', - platform_schema=platform_schema)) + MockPlatform(platform_schema=platform_schema)) with assert_setup_component(1): assert setup.setup_component(self.hass, 'platform_conf', { @@ -322,14 +317,15 @@ def test_validate_platform_config_4(self): def test_component_not_found(self): """setup_component should not crash if component doesn't exist.""" - assert not setup.setup_component(self.hass, 'non_existing') + assert setup.setup_component(self.hass, 'non_existing') is False def test_component_not_double_initialized(self): """Test we do not set up a component twice.""" mock_setup = mock.MagicMock(return_value=True) - loader.set_component( - self.hass, 'comp', MockModule('comp', setup=mock_setup)) + mock_integration( + self.hass, + MockModule('comp', setup=mock_setup)) assert setup.setup_component(self.hass, 'comp') assert mock_setup.called @@ -344,9 +340,9 @@ def test_component_not_double_initialized(self): def test_component_not_installed_if_requirement_fails(self, mock_install): """Component setup should fail if requirement can't install.""" self.hass.config.skip_pip = False - loader.set_component( + mock_integration( self.hass, - 'comp', MockModule('comp', requirements=['package==0.0.1'])) + MockModule('comp', requirements=['package==0.0.1'])) assert not setup.setup_component(self.hass, 'comp') assert 'comp' not in self.hass.config.components @@ -360,9 +356,9 @@ def async_setup(hass, config): """Tracking Setup.""" result.append(1) - loader.set_component( + mock_integration( self.hass, - 'comp', MockModule('comp', async_setup=async_setup)) + MockModule('comp', async_setup=async_setup)) def setup_component(): """Set up the component.""" @@ -393,8 +389,8 @@ def test_component_not_setup_missing_dependencies(self): def test_component_failing_setup(self): """Test component that fails setup.""" - loader.set_component( - self.hass, 'comp', + mock_integration( + self.hass, MockModule('comp', setup=lambda hass, config: False)) assert not setup.setup_component(self.hass, 'comp', {}) @@ -406,8 +402,8 @@ def exception_setup(hass, config): """Raise exception.""" raise Exception('fail!') - loader.set_component( - self.hass, 'comp', MockModule('comp', setup=exception_setup)) + mock_integration(self.hass, + MockModule('comp', setup=exception_setup)) assert not setup.setup_component(self.hass, 'comp', {}) assert 'comp' not in self.hass.config.components @@ -420,12 +416,18 @@ def config_check_setup(hass, config): return True raise Exception('Config not passed in: {}'.format(config)) - loader.set_component( - self.hass, 'comp_a', - MockModule('comp_a', setup=config_check_setup)) + platform = MockPlatform() - loader.set_component( - self.hass, 'switch.platform_a', MockPlatform('comp_b', ['comp_a'])) + mock_integration(self.hass, + MockModule('comp_a', setup=config_check_setup)) + mock_integration( + self.hass, + MockModule('platform_a', + setup=config_check_setup, + dependencies=['comp_a']), + ) + + mock_entity_platform(self.hass, 'switch.platform_a', platform) setup.setup_component(self.hass, 'switch', { 'comp_a': { @@ -445,7 +447,7 @@ def test_platform_specific_config_validation(self): mock_setup = mock.MagicMock(spec_set=True) - loader.set_component( + mock_entity_platform( self.hass, 'switch.platform_a', MockPlatform(platform_schema=platform_schema, @@ -476,7 +478,7 @@ def test_platform_specific_config_validation(self): self.hass.data.pop(setup.DATA_SETUP) self.hass.config.components.remove('switch') - with assert_setup_component(1): + with assert_setup_component(1, 'switch'): assert setup.setup_component(self.hass, 'switch', { 'switch': { 'platform': 'platform_a', @@ -487,9 +489,8 @@ def test_platform_specific_config_validation(self): def test_disable_component_if_invalid_return(self): """Test disabling component if invalid return.""" - loader.set_component( + mock_integration( self.hass, - 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: None)) assert not setup.setup_component(self.hass, 'disabled_component') @@ -497,9 +498,8 @@ def test_disable_component_if_invalid_return(self): assert 'disabled_component' not in self.hass.config.components self.hass.data.pop(setup.DATA_SETUP) - loader.set_component( + mock_integration( self.hass, - 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: False)) assert not setup.setup_component(self.hass, 'disabled_component') @@ -508,9 +508,8 @@ def test_disable_component_if_invalid_return(self): assert 'disabled_component' not in self.hass.config.components self.hass.data.pop(setup.DATA_SETUP) - loader.set_component( + mock_integration( self.hass, - 'disabled_component', MockModule('disabled_component', setup=lambda hass, config: True)) assert setup.setup_component(self.hass, 'disabled_component') @@ -535,19 +534,16 @@ def component_track_setup(hass, config): call_order.append(1) return True - loader.set_component( + mock_integration( self.hass, - 'test_component1', MockModule('test_component1', setup=component1_setup)) - loader.set_component( + mock_integration( self.hass, - 'test_component2', MockModule('test_component2', setup=component_track_setup)) - loader.set_component( + mock_integration( self.hass, - 'test_component3', MockModule('test_component3', setup=component_track_setup)) @callback @@ -575,8 +571,7 @@ def test_component_cannot_depend_config(hass): @asyncio.coroutine def test_component_warn_slow_setup(hass): """Warn we log when a component setup takes a long time.""" - loader.set_component( - hass, 'test_component1', MockModule('test_component1')) + mock_integration(hass, MockModule('test_component1')) with mock.patch.object(hass.loop, 'call_later', mock.MagicMock()) \ as mock_call: result = yield from setup.async_setup_component( @@ -596,8 +591,8 @@ def test_component_warn_slow_setup(hass): @asyncio.coroutine def test_platform_no_warn_slow(hass): """Do not warn for long entity setup time.""" - loader.set_component( - hass, 'test_component1', + mock_integration( + hass, MockModule('test_component1', platform_schema=PLATFORM_SCHEMA)) with mock.patch.object(hass.loop, 'call_later', mock.MagicMock()) \ as mock_call: diff --git a/tests/testing_config/__init__.py b/tests/testing_config/__init__.py new file mode 100644 index 00000000000000..98d2bc1bc8d3e4 --- /dev/null +++ b/tests/testing_config/__init__.py @@ -0,0 +1 @@ +"""Configuration that's used when running tests.""" diff --git a/tests/testing_config/custom_components/__init__.py b/tests/testing_config/custom_components/__init__.py new file mode 100644 index 00000000000000..f84ba5808ae6d2 --- /dev/null +++ b/tests/testing_config/custom_components/__init__.py @@ -0,0 +1 @@ +"""A collection of custom integrations used when running tests.""" diff --git a/tests/testing_config/custom_components/switch/test_embedded.py b/tests/testing_config/custom_components/switch/test_legacy.py similarity index 100% rename from tests/testing_config/custom_components/switch/test_embedded.py rename to tests/testing_config/custom_components/switch/test_legacy.py diff --git a/tests/testing_config/custom_components/switch/.translations/test.de.json b/tests/testing_config/custom_components/test/.translations/switch.de.json similarity index 100% rename from tests/testing_config/custom_components/switch/.translations/test.de.json rename to tests/testing_config/custom_components/test/.translations/switch.de.json diff --git a/tests/testing_config/custom_components/switch/.translations/test.en.json b/tests/testing_config/custom_components/test/.translations/switch.en.json similarity index 100% rename from tests/testing_config/custom_components/switch/.translations/test.en.json rename to tests/testing_config/custom_components/test/.translations/switch.en.json diff --git a/tests/testing_config/custom_components/switch/.translations/test.es.json b/tests/testing_config/custom_components/test/.translations/switch.es.json similarity index 100% rename from tests/testing_config/custom_components/switch/.translations/test.es.json rename to tests/testing_config/custom_components/test/.translations/switch.es.json diff --git a/tests/testing_config/custom_components/test/__init__.py b/tests/testing_config/custom_components/test/__init__.py new file mode 100644 index 00000000000000..206c97f847b16a --- /dev/null +++ b/tests/testing_config/custom_components/test/__init__.py @@ -0,0 +1 @@ +"""An integration with several platforms used with unit tests.""" diff --git a/tests/testing_config/custom_components/device_tracker/test.py b/tests/testing_config/custom_components/test/device_tracker.py similarity index 100% rename from tests/testing_config/custom_components/device_tracker/test.py rename to tests/testing_config/custom_components/test/device_tracker.py diff --git a/tests/testing_config/custom_components/image_processing/test.py b/tests/testing_config/custom_components/test/image_processing.py similarity index 100% rename from tests/testing_config/custom_components/image_processing/test.py rename to tests/testing_config/custom_components/test/image_processing.py diff --git a/tests/testing_config/custom_components/light/test.py b/tests/testing_config/custom_components/test/light.py similarity index 100% rename from tests/testing_config/custom_components/light/test.py rename to tests/testing_config/custom_components/test/light.py diff --git a/tests/testing_config/custom_components/test/manifest.json b/tests/testing_config/custom_components/test/manifest.json new file mode 100644 index 00000000000000..70882fece0582f --- /dev/null +++ b/tests/testing_config/custom_components/test/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "test", + "name": "Test Components", + "documentation": "http://example.com", + "requirements": [], + "dependencies": [], + "codeowners": [] +} diff --git a/tests/testing_config/custom_components/switch/test.py b/tests/testing_config/custom_components/test/switch.py similarity index 100% rename from tests/testing_config/custom_components/switch/test.py rename to tests/testing_config/custom_components/test/switch.py From d078e50fb88ed750de2e9627f6ebaf3e1d6d1f36 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Thu, 11 Apr 2019 10:49:02 +0200 Subject: [PATCH 574/605] Add device HmIP-MIOB to Homematic IP Cloud (#22975) * Update upstream dependency * Add two switches --- homeassistant/components/homematicip_cloud/__init__.py | 2 +- homeassistant/components/homematicip_cloud/manifest.json | 2 +- homeassistant/components/homematicip_cloud/switch.py | 4 ++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/__init__.py b/homeassistant/components/homematicip_cloud/__init__.py index ac93ef05b85268..1330a2750ae6db 100644 --- a/homeassistant/components/homematicip_cloud/__init__.py +++ b/homeassistant/components/homematicip_cloud/__init__.py @@ -15,7 +15,7 @@ from .device import HomematicipGenericDevice # noqa: F401 from .hap import HomematicipAuth, HomematicipHAP # noqa: F401 -REQUIREMENTS = ['homematicip==0.10.6'] +REQUIREMENTS = ['homematicip==0.10.7'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index 622928e8629663..030b4d5b79ba20 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -3,7 +3,7 @@ "name": "Homematicip cloud", "documentation": "https://www.home-assistant.io/components/homematicip_cloud", "requirements": [ - "homematicip==0.10.6" + "homematicip==0.10.7" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index 2199b867002b57..f9713cd8c006cc 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -25,6 +25,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): AsyncBrandSwitchMeasuring, AsyncFullFlushSwitchMeasuring, AsyncOpenCollector8Module, + AsyncMultiIOBox, ) from homematicip.aio.group import AsyncSwitchingGroup @@ -45,6 +46,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): elif isinstance(device, AsyncOpenCollector8Module): for channel in range(1, 9): devices.append(HomematicipMultiSwitch(home, device, channel)) + elif isinstance(device, AsyncMultiIOBox): + for channel in range(1, 3): + devices.append(HomematicipMultiSwitch(home, device, channel)) for group in home.groups: if isinstance(group, AsyncSwitchingGroup): diff --git a/requirements_all.txt b/requirements_all.txt index 922239c44ef0c2..5a1367f7d137a6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -551,7 +551,7 @@ homeassistant-pyozw==0.1.4 homekit[IP]==0.13.0 # homeassistant.components.homematicip_cloud -homematicip==0.10.6 +homematicip==0.10.7 # homeassistant.components.horizon horimote==0.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c69d4026227937..25b4ecaf6d02f3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -139,7 +139,7 @@ home-assistant-frontend==20190331.0 homekit[IP]==0.13.0 # homeassistant.components.homematicip_cloud -homematicip==0.10.6 +homematicip==0.10.7 # homeassistant.components.google # homeassistant.components.remember_the_milk From 02347df1405f74232d7b094d81d38e0123357b22 Mon Sep 17 00:00:00 2001 From: Stephan Beier Date: Thu, 11 Apr 2019 18:32:25 +0200 Subject: [PATCH 575/605] Prevent the projector to toogle on/off (#22985) --- homeassistant/components/epson/media_player.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/epson/media_player.py b/homeassistant/components/epson/media_player.py index 75be4f7fe2c1b7..57bd18e0ee0e50 100644 --- a/homeassistant/components/epson/media_player.py +++ b/homeassistant/components/epson/media_player.py @@ -137,12 +137,14 @@ def supported_features(self): async def async_turn_on(self): """Turn on epson.""" from epson_projector.const import TURN_ON - await self._projector.send_command(TURN_ON) + if self._state == STATE_OFF: + await self._projector.send_command(TURN_ON) async def async_turn_off(self): """Turn off epson.""" from epson_projector.const import TURN_OFF - await self._projector.send_command(TURN_OFF) + if self._state == STATE_ON: + await self._projector.send_command(TURN_OFF) @property def source_list(self): From ac7f1a7a37c7d46f1d2fefb66a4654180c084363 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 11 Apr 2019 12:52:02 -0700 Subject: [PATCH 576/605] Fix test failed in py35 (#23002) --- tests/components/demo/test_geo_location.py | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/components/demo/test_geo_location.py b/tests/components/demo/test_geo_location.py index acfc97b00e68f7..9cade83285f709 100644 --- a/tests/components/demo/test_geo_location.py +++ b/tests/components/demo/test_geo_location.py @@ -45,18 +45,22 @@ def test_setup_platform(self): all_states = self.hass.states.all() assert len(all_states) == NUMBER_OF_DEMO_DEVICES + 1 - # Check a single device's attributes. - state_first_entry = all_states[1] # 0 is zone - assert abs( - state_first_entry.attributes['latitude'] - - self.hass.config.latitude - ) < 1.0 - assert abs( - state_first_entry.attributes['longitude'] - - self.hass.config.longitude - ) < 1.0 - assert state_first_entry.attributes['unit_of_measurement'] == \ - DEFAULT_UNIT_OF_MEASUREMENT + for state in all_states: + # Check a single device's attributes. + if state.domain != geo_location.DOMAIN: + # ignore home zone state + continue + assert abs( + state.attributes['latitude'] - + self.hass.config.latitude + ) < 1.0 + assert abs( + state.attributes['longitude'] - + self.hass.config.longitude + ) < 1.0 + assert state.attributes['unit_of_measurement'] == \ + DEFAULT_UNIT_OF_MEASUREMENT + # Update (replaces 1 device). fire_time_changed(self.hass, utcnow + DEFAULT_UPDATE_INTERVAL) self.hass.block_till_done() From 8bfe77a1a0b8d9b8173e460a4173d8ed5f3745b3 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Thu, 11 Apr 2019 17:57:48 -0500 Subject: [PATCH 577/605] Add aftership package details and add/remove services (#22275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📦 Package details and add/remove services * lint * Cleanup * lint * Fix add tracking service call * cleanup * Make data easier to consume on the front-end * lint * Add expected delivery date * call update after add/remove * lint * cleanup * Add last_checkpoint * address review comments * remove formatting changes * lint * Address review comments * address review comments * address review comments * lint * lint --- homeassistant/components/aftership/const.py | 2 + homeassistant/components/aftership/sensor.py | 99 +++++++++++++++++-- .../components/aftership/services.yaml | 24 +++++ 3 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 homeassistant/components/aftership/const.py create mode 100644 homeassistant/components/aftership/services.yaml diff --git a/homeassistant/components/aftership/const.py b/homeassistant/components/aftership/const.py new file mode 100644 index 00000000000000..e096aa14911e7a --- /dev/null +++ b/homeassistant/components/aftership/const.py @@ -0,0 +1,2 @@ +"""Constants for the Aftership integration.""" +DOMAIN = 'aftership' diff --git a/homeassistant/components/aftership/sensor.py b/homeassistant/components/aftership/sensor.py index 18bc3cb34304bc..eefbb299a07125 100644 --- a/homeassistant/components/aftership/sensor.py +++ b/homeassistant/components/aftership/sensor.py @@ -8,24 +8,46 @@ from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle +from .const import DOMAIN REQUIREMENTS = ['pyaftership==0.1.2'] _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Information provided by AfterShip' +ATTR_TRACKINGS = 'trackings' + +BASE = 'https://track.aftership.com/' CONF_SLUG = 'slug' CONF_TITLE = 'title' CONF_TRACKING_NUMBER = 'tracking_number' DEFAULT_NAME = 'aftership' +UPDATE_TOPIC = DOMAIN + '_update' ICON = 'mdi:package-variant-closed' -MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) + +SERVICE_ADD_TRACKING = 'add_tracking' +SERVICE_REMOVE_TRACKING = 'remove_tracking' + +ADD_TRACKING_SERVICE_SCHEMA = vol.Schema( + { + vol.Required(CONF_TRACKING_NUMBER): cv.string, + vol.Optional(CONF_TITLE): cv.string, + vol.Optional(CONF_SLUG): cv.string, + } +) + +REMOVE_TRACKING_SERVICE_SCHEMA = vol.Schema( + {vol.Required(CONF_SLUG): cv.string, + vol.Required(CONF_TRACKING_NUMBER): cv.string} +) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, @@ -51,7 +73,40 @@ async def async_setup_platform( aftership.meta) return - async_add_entities([AfterShipSensor(aftership, name)], True) + instance = AfterShipSensor(aftership, name) + + async_add_entities([instance], True) + + async def handle_add_tracking(call): + """Call when a user adds a new Aftership tracking from HASS.""" + title = call.data.get(CONF_TITLE) + slug = call.data.get(CONF_SLUG) + tracking_number = call.data[CONF_TRACKING_NUMBER] + + await aftership.add_package_tracking(tracking_number, title, slug) + async_dispatcher_send(hass, UPDATE_TOPIC) + + hass.services.async_register( + DOMAIN, + SERVICE_ADD_TRACKING, + handle_add_tracking, + schema=ADD_TRACKING_SERVICE_SCHEMA, + ) + + async def handle_remove_tracking(call): + """Call when a user removes an Aftership tracking from HASS.""" + slug = call.data[CONF_SLUG] + tracking_number = call.data[CONF_TRACKING_NUMBER] + + await aftership.remove_package_tracking(slug, tracking_number) + async_dispatcher_send(hass, UPDATE_TOPIC) + + hass.services.async_register( + DOMAIN, + SERVICE_REMOVE_TRACKING, + handle_remove_tracking, + schema=REMOVE_TRACKING_SERVICE_SCHEMA, + ) class AfterShipSensor(Entity): @@ -89,8 +144,18 @@ def icon(self): """Icon to use in the frontend.""" return ICON + async def async_added_to_hass(self): + """Register callbacks.""" + self.hass.helpers.dispatcher.async_dispatcher_connect( + UPDATE_TOPIC, self.force_update) + + async def force_update(self): + """Force update of data.""" + await self.async_update(no_throttle=True) + await self.async_update_ha_state() + @Throttle(MIN_TIME_BETWEEN_UPDATES) - async def async_update(self): + async def async_update(self, **kwargs): """Get the latest data from the AfterShip API.""" await self.aftership.get_trackings() @@ -104,12 +169,29 @@ async def async_update(self): status_to_ignore = {'delivered'} status_counts = {} + trackings = [] not_delivered_count = 0 - for tracking in self.aftership.trackings['trackings']: - status = tracking['tag'].lower() - name = tracking['tracking_number'] - status_counts[status] = status_counts.get(status, 0)+1 + for track in self.aftership.trackings['trackings']: + status = track['tag'].lower() + name = ( + track['tracking_number'] + if track['title'] is None + else track['title'] + ) + status_counts[status] = status_counts.get(status, 0) + 1 + trackings.append({ + 'name': name, + 'tracking_number': track['tracking_number'], + 'slug': track['slug'], + 'link': '%s%s/%s' % + (BASE, track['slug'], track['tracking_number']), + 'last_update': track['updated_at'], + 'expected_delivery': track['expected_delivery'], + 'status': track['tag'], + 'last_checkpoint': track['checkpoints'][-1] + }) + if status not in status_to_ignore: not_delivered_count += 1 else: @@ -117,7 +199,8 @@ async def async_update(self): self._attributes = { ATTR_ATTRIBUTION: ATTRIBUTION, - **status_counts + **status_counts, + ATTR_TRACKINGS: trackings, } self._state = not_delivered_count diff --git a/homeassistant/components/aftership/services.yaml b/homeassistant/components/aftership/services.yaml new file mode 100644 index 00000000000000..157156c3252900 --- /dev/null +++ b/homeassistant/components/aftership/services.yaml @@ -0,0 +1,24 @@ +# Describes the format for available aftership services + +add_tracking: + description: Add new tracking to Aftership. + fields: + tracking_number: + description: Tracking number for the new tracking + example: '123456789' + title: + description: A custom title for the new tracking + example: 'Laptop' + slug: + description: Slug (carrier) of the new tracking + example: 'USPS' + +remove_tracking: + description: Remove a tracking from Aftership. + fields: + tracking_number: + description: Tracking number of the tracking to remove + example: '123456789' + slug: + description: Slug (carrier) of the tracking to remove + example: 'USPS' From 7303d56a55cfaf65decb80fadf50c922e64daebf Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Thu, 11 Apr 2019 19:06:36 -0700 Subject: [PATCH 578/605] Mobile App: Remove component loading support (#23025) Loading a component defined in a registration didn't actually work and was undocumented, so let's just remove it instead of fixing #23005. ## Checklist: - [X] The code change is tested and works locally. - [X] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass** - [X] There is no commented out code in this PR. --- homeassistant/components/mobile_app/const.py | 3 --- .../components/mobile_app/http_api.py | 26 +++---------------- tests/components/mobile_app/test_http_api.py | 25 ------------------ 3 files changed, 4 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index 05d240da909db8..8b33406216e496 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -28,7 +28,6 @@ DATA_SENSOR = 'sensor' DATA_STORE = 'store' -ATTR_APP_COMPONENT = 'app_component' ATTR_APP_DATA = 'app_data' ATTR_APP_ID = 'app_id' ATTR_APP_NAME = 'app_name' @@ -66,7 +65,6 @@ ATTR_WEBHOOK_TYPE = 'type' ERR_ENCRYPTION_REQUIRED = 'encryption_required' -ERR_INVALID_COMPONENT = 'invalid_component' ERR_SENSOR_NOT_REGISTERED = 'not_registered' ERR_SENSOR_DUPLICATE_UNIQUE_ID = 'duplicate_unique_id' @@ -89,7 +87,6 @@ REGISTRATION_SCHEMA = vol.Schema({ - vol.Optional(ATTR_APP_COMPONENT): cv.string, vol.Optional(ATTR_APP_DATA, default={}): dict, vol.Required(ATTR_APP_ID): cv.string, vol.Required(ATTR_APP_NAME): cv.string, diff --git a/homeassistant/components/mobile_app/http_api.py b/homeassistant/components/mobile_app/http_api.py index 2ae8f441e52399..8d63e797e0f5a6 100644 --- a/homeassistant/components/mobile_app/http_api.py +++ b/homeassistant/components/mobile_app/http_api.py @@ -12,15 +12,11 @@ from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.const import (HTTP_CREATED, CONF_WEBHOOK_ID) -from homeassistant.loader import get_component +from .const import (ATTR_DEVICE_ID, ATTR_SUPPORTS_ENCRYPTION, + CONF_CLOUDHOOK_URL, CONF_REMOTE_UI_URL, CONF_SECRET, + CONF_USER_ID, DOMAIN, REGISTRATION_SCHEMA) -from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_ID, - ATTR_SUPPORTS_ENCRYPTION, CONF_CLOUDHOOK_URL, - CONF_REMOTE_UI_URL, CONF_SECRET, - CONF_USER_ID, DOMAIN, ERR_INVALID_COMPONENT, - REGISTRATION_SCHEMA) - -from .helpers import error_response, supports_encryption +from .helpers import supports_encryption class RegistrationsView(HomeAssistantView): @@ -34,20 +30,6 @@ async def post(self, request: Request, data: Dict) -> Response: """Handle the POST request for registration.""" hass = request.app['hass'] - if ATTR_APP_COMPONENT in data: - component = get_component(hass, data[ATTR_APP_COMPONENT]) - if component is None: - fmt_str = "{} is not a valid component." - msg = fmt_str.format(data[ATTR_APP_COMPONENT]) - return error_response(ERR_INVALID_COMPONENT, msg) - - if (hasattr(component, 'DEPENDENCIES') is False or - (hasattr(component, 'DEPENDENCIES') and - DOMAIN not in component.DEPENDENCIES)): - fmt_str = "{} is not compatible with mobile_app." - msg = fmt_str.format(data[ATTR_APP_COMPONENT]) - return error_response(ERR_INVALID_COMPONENT, msg) - webhook_id = generate_secret() if hass.components.cloud.async_active_subscription(): diff --git a/tests/components/mobile_app/test_http_api.py b/tests/components/mobile_app/test_http_api.py index eb9d1f54d93c95..dc51b850a16e50 100644 --- a/tests/components/mobile_app/test_http_api.py +++ b/tests/components/mobile_app/test_http_api.py @@ -80,28 +80,3 @@ async def test_registration(hass, hass_client): # noqa: F811 decrypted_data = decrypted_data.decode("utf-8") assert json.loads(decrypted_data) == {'one': 'Hello world'} - - -async def test_register_invalid_component(authed_api_client): # noqa: F811 - """Test that registration with invalid component fails.""" - resp = await authed_api_client.post( - '/api/mobile_app/registrations', json={ - 'app_component': 'will_never_be_valid', - 'app_data': {'foo': 'bar'}, - 'app_id': 'io.homeassistant.mobile_app_test', - 'app_name': 'Mobile App Tests', - 'app_version': '1.0.0', - 'device_name': 'Test 1', - 'manufacturer': 'mobile_app', - 'model': 'Test', - 'os_name': 'Linux', - 'os_version': '1.0', - 'supports_encryption': True - } - ) - - assert resp.status == 400 - register_json = await resp.json() - assert 'error' in register_json - assert register_json['success'] is False - assert register_json['error']['code'] == 'invalid_component' From 57f17707c63aaba0fc6907209b747c530a37dc0f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 11 Apr 2019 20:11:56 -0700 Subject: [PATCH 579/605] Specify configurator as dependency (#23030) --- homeassistant/components/apple_tv/manifest.json | 2 +- homeassistant/components/august/manifest.json | 2 +- homeassistant/components/automatic/manifest.json | 1 + homeassistant/components/braviatv/manifest.json | 4 +++- homeassistant/components/ecobee/manifest.json | 2 +- homeassistant/components/fitbit/manifest.json | 1 + homeassistant/components/gpmdp/manifest.json | 2 +- homeassistant/components/homekit_controller/manifest.json | 2 +- homeassistant/components/icloud/manifest.json | 2 +- homeassistant/components/plex/manifest.json | 2 +- homeassistant/components/remember_the_milk/manifest.json | 2 +- homeassistant/components/sabnzbd/manifest.json | 2 +- homeassistant/components/spotify/manifest.json | 1 + homeassistant/components/webostv/manifest.json | 2 +- homeassistant/components/wink/manifest.json | 2 +- 15 files changed, 17 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/apple_tv/manifest.json b/homeassistant/components/apple_tv/manifest.json index 4f27fde2aa324a..f21de7333767fc 100644 --- a/homeassistant/components/apple_tv/manifest.json +++ b/homeassistant/components/apple_tv/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "pyatv==0.3.12" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index 39bc70fba7bf71..e41491c4b0ac85 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "py-august==0.7.0" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/automatic/manifest.json b/homeassistant/components/automatic/manifest.json index 50bd02d2ac180c..9743835af20ab2 100644 --- a/homeassistant/components/automatic/manifest.json +++ b/homeassistant/components/automatic/manifest.json @@ -6,6 +6,7 @@ "aioautomatic==0.6.5" ], "dependencies": [ + "configurator", "http" ], "codeowners": [ diff --git a/homeassistant/components/braviatv/manifest.json b/homeassistant/components/braviatv/manifest.json index 35e2698af4d26d..d8a835676b8075 100644 --- a/homeassistant/components/braviatv/manifest.json +++ b/homeassistant/components/braviatv/manifest.json @@ -5,7 +5,9 @@ "requirements": [ "braviarc-homeassistant==0.3.7.dev0" ], - "dependencies": [], + "dependencies": [ + "configurator" + ], "codeowners": [ "@robbiet480" ] diff --git a/homeassistant/components/ecobee/manifest.json b/homeassistant/components/ecobee/manifest.json index 3c7275a389512d..d2aa7f0b515c17 100644 --- a/homeassistant/components/ecobee/manifest.json +++ b/homeassistant/components/ecobee/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "python-ecobee-api==0.0.18" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/fitbit/manifest.json b/homeassistant/components/fitbit/manifest.json index 5b02bca4b6f0ed..baf0d8aaed1db4 100644 --- a/homeassistant/components/fitbit/manifest.json +++ b/homeassistant/components/fitbit/manifest.json @@ -6,6 +6,7 @@ "fitbit==0.3.0" ], "dependencies": [ + "configurator", "http" ], "codeowners": [ diff --git a/homeassistant/components/gpmdp/manifest.json b/homeassistant/components/gpmdp/manifest.json index 97e97e7645cf3b..98ab8035023d04 100644 --- a/homeassistant/components/gpmdp/manifest.json +++ b/homeassistant/components/gpmdp/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "websocket-client==0.54.0" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index e641f87e2a3472..e724f680b609a5 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "homekit[IP]==0.13.0" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/icloud/manifest.json b/homeassistant/components/icloud/manifest.json index 865d64c6860b5a..5f2075a0fd6316 100644 --- a/homeassistant/components/icloud/manifest.json +++ b/homeassistant/components/icloud/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "pyicloud==0.9.1" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index ae8e1b684ed139..32ddb83476c81e 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "plexapi==3.0.6" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/remember_the_milk/manifest.json b/homeassistant/components/remember_the_milk/manifest.json index a2076eb5800de4..c9d35e9d2c9d7d 100644 --- a/homeassistant/components/remember_the_milk/manifest.json +++ b/homeassistant/components/remember_the_milk/manifest.json @@ -6,6 +6,6 @@ "RtmAPI==0.7.0", "httplib2==0.10.3" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/sabnzbd/manifest.json b/homeassistant/components/sabnzbd/manifest.json index ae03895f415b81..9424e5f3a1a591 100644 --- a/homeassistant/components/sabnzbd/manifest.json +++ b/homeassistant/components/sabnzbd/manifest.json @@ -5,6 +5,6 @@ "requirements": [ "pysabnzbd==1.1.0" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json index a371f05629e959..366a5eef0ad99e 100644 --- a/homeassistant/components/spotify/manifest.json +++ b/homeassistant/components/spotify/manifest.json @@ -6,6 +6,7 @@ "spotipy-homeassistant==2.4.4.dev1" ], "dependencies": [ + "configurator", "http" ], "codeowners": [] diff --git a/homeassistant/components/webostv/manifest.json b/homeassistant/components/webostv/manifest.json index 0673c36e91f2a9..4dd2f92628d630 100644 --- a/homeassistant/components/webostv/manifest.json +++ b/homeassistant/components/webostv/manifest.json @@ -6,6 +6,6 @@ "pylgtv==0.1.9", "websockets==6.0" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } diff --git a/homeassistant/components/wink/manifest.json b/homeassistant/components/wink/manifest.json index 6ad6fa2b940966..c8951637bde445 100644 --- a/homeassistant/components/wink/manifest.json +++ b/homeassistant/components/wink/manifest.json @@ -6,6 +6,6 @@ "pubnubsub-handler==1.0.3", "python-wink==1.10.3" ], - "dependencies": [], + "dependencies": ["configurator"], "codeowners": [] } From c94b031db1b382f6e6631a1696ad1f9b6077e89e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 11 Apr 2019 23:37:45 -0700 Subject: [PATCH 580/605] Fix unnecessary hass.components interaction (#23029) * Fix wemo * Fix bloomsky * Fix netatmo * Fix one more reference --- .../components/bloomsky/binary_sensor.py | 7 ++++--- homeassistant/components/bloomsky/camera.py | 7 ++++--- homeassistant/components/bloomsky/sensor.py | 9 +++++---- .../components/netatmo/binary_sensor.py | 5 ++--- homeassistant/components/netatmo/camera.py | 5 ++--- homeassistant/components/netatmo/climate.py | 8 ++++---- homeassistant/components/netatmo/sensor.py | 17 ++++++++--------- homeassistant/components/wemo/binary_sensor.py | 4 +++- homeassistant/components/wemo/fan.py | 4 +++- homeassistant/components/wemo/light.py | 4 +++- homeassistant/components/wemo/switch.py | 4 +++- 11 files changed, 41 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/bloomsky/binary_sensor.py b/homeassistant/components/bloomsky/binary_sensor.py index c8763524de7631..8d4a89a017908c 100644 --- a/homeassistant/components/bloomsky/binary_sensor.py +++ b/homeassistant/components/bloomsky/binary_sensor.py @@ -8,6 +8,8 @@ from homeassistant.const import CONF_MONITORED_CONDITIONS import homeassistant.helpers.config_validation as cv +from . import BLOOMSKY + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['bloomsky'] @@ -25,14 +27,13 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available BloomSky weather binary sensors.""" - bloomsky = hass.components.bloomsky # Default needed in case of discovery sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES) - for device in bloomsky.BLOOMSKY.devices.values(): + for device in BLOOMSKY.devices.values(): for variable in sensors: add_entities( - [BloomSkySensor(bloomsky.BLOOMSKY, device, variable)], True) + [BloomSkySensor(BLOOMSKY, device, variable)], True) class BloomSkySensor(BinarySensorDevice): diff --git a/homeassistant/components/bloomsky/camera.py b/homeassistant/components/bloomsky/camera.py index 5cb2e1adfe16ba..a2e1d8e2d3a59d 100644 --- a/homeassistant/components/bloomsky/camera.py +++ b/homeassistant/components/bloomsky/camera.py @@ -5,14 +5,15 @@ from homeassistant.components.camera import Camera +from . import BLOOMSKY + DEPENDENCIES = ['bloomsky'] def setup_platform(hass, config, add_entities, discovery_info=None): """Set up access to BloomSky cameras.""" - bloomsky = hass.components.bloomsky - for device in bloomsky.BLOOMSKY.devices.values(): - add_entities([BloomSkyCamera(bloomsky.BLOOMSKY, device)]) + for device in BLOOMSKY.devices.values(): + add_entities([BloomSkyCamera(BLOOMSKY, device)]) class BloomSkyCamera(Camera): diff --git a/homeassistant/components/bloomsky/sensor.py b/homeassistant/components/bloomsky/sensor.py index 7e6847f0e7ec2e..6909c57eec4e87 100644 --- a/homeassistant/components/bloomsky/sensor.py +++ b/homeassistant/components/bloomsky/sensor.py @@ -8,7 +8,9 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -_LOGGER = logging.getLogger(__name__) +from . import BLOOMSKY + +LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['bloomsky'] @@ -38,14 +40,13 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available BloomSky weather sensors.""" - bloomsky = hass.components.bloomsky # Default needed in case of discovery sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES) - for device in bloomsky.BLOOMSKY.devices.values(): + for device in BLOOMSKY.devices.values(): for variable in sensors: add_entities( - [BloomSkySensor(bloomsky.BLOOMSKY, device, variable)], True) + [BloomSkySensor(BLOOMSKY, device, variable)], True) class BloomSkySensor(Entity): diff --git a/homeassistant/components/netatmo/binary_sensor.py b/homeassistant/components/netatmo/binary_sensor.py index a11ce6bddf710a..7c2b1a73a4dcd8 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -8,7 +8,7 @@ from homeassistant.const import CONF_TIMEOUT from homeassistant.helpers import config_validation as cv -from . import CameraData +from . import CameraData, NETATMO_AUTH _LOGGER = logging.getLogger(__name__) @@ -53,7 +53,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the access to Netatmo binary sensor.""" - netatmo = hass.components.netatmo home = config.get(CONF_HOME) timeout = config.get(CONF_TIMEOUT) if timeout is None: @@ -63,7 +62,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): import pyatmo try: - data = CameraData(hass, netatmo.NETATMO_AUTH, home) + data = CameraData(hass, NETATMO_AUTH, home) if not data.get_camera_names(): return None except pyatmo.NoDevice: diff --git a/homeassistant/components/netatmo/camera.py b/homeassistant/components/netatmo/camera.py index 6b80c3061b567e..c8a540be6dd950 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -9,7 +9,7 @@ from homeassistant.const import CONF_VERIFY_SSL from homeassistant.helpers import config_validation as cv -from . import CameraData +from . import CameraData, NETATMO_AUTH DEPENDENCIES = ['netatmo'] @@ -35,13 +35,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up access to Netatmo cameras.""" - netatmo = hass.components.netatmo home = config.get(CONF_HOME) verify_ssl = config.get(CONF_VERIFY_SSL, True) quality = config.get(CONF_QUALITY, DEFAULT_QUALITY) import pyatmo try: - data = CameraData(hass, netatmo.NETATMO_AUTH, home) + data = CameraData(hass, NETATMO_AUTH, home) for camera_name in data.get_camera_names(): camera_type = data.get_camera_type(camera=camera_name, home=home) if CONF_CAMERAS in config: diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index d0537c5912b18a..5defbbf22e3e33 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -14,6 +14,8 @@ STATE_OFF, TEMP_CELSIUS, ATTR_TEMPERATURE, CONF_NAME) from homeassistant.util import Throttle +from . import NETATMO_AUTH + DEPENDENCIES = ['netatmo'] _LOGGER = logging.getLogger(__name__) @@ -66,12 +68,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the NetAtmo Thermostat.""" - netatmo = hass.components.netatmo - import pyatmo homes_conf = config.get(CONF_HOMES) try: - home_data = HomeData(netatmo.NETATMO_AUTH) + home_data = HomeData(NETATMO_AUTH) except pyatmo.NoDevice: return @@ -90,7 +90,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): for home in homes: _LOGGER.debug("Setting up %s ...", home) try: - room_data = ThermostatData(netatmo.NETATMO_AUTH, home) + room_data = ThermostatData(NETATMO_AUTH, home) except pyatmo.NoDevice: continue for room_id in room_data.get_room_ids(): diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 307b76ca434bd4..2ce4b6e6ce2a38 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -12,6 +12,8 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv +from . import NETATMO_AUTH + _LOGGER = logging.getLogger(__name__) CONF_MODULES = 'modules' @@ -67,26 +69,24 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available Netatmo weather sensors.""" - netatmo = hass.components.netatmo - dev = [] if CONF_MODULES in config: - manual_config(netatmo, config, dev) + manual_config(config, dev) else: - auto_config(netatmo, config, dev) + auto_config(config, dev) if dev: add_entities(dev, True) -def manual_config(netatmo, config, dev): +def manual_config(config, dev): """Handle manual configuration.""" import pyatmo all_classes = all_product_classes() not_handled = {} for data_class in all_classes: - data = NetAtmoData(netatmo.NETATMO_AUTH, data_class, + data = NetAtmoData(NETATMO_AUTH, data_class, config.get(CONF_STATION)) try: # Iterate each module @@ -109,13 +109,12 @@ def manual_config(netatmo, config, dev): _LOGGER.error('Module name: "%s" not found', module_name) -def auto_config(netatmo, config, dev): +def auto_config(config, dev): """Handle auto configuration.""" import pyatmo for data_class in all_product_classes(): - data = NetAtmoData(netatmo.NETATMO_AUTH, data_class, - config.get(CONF_STATION)) + data = NetAtmoData(NETATMO_AUTH, data_class, config.get(CONF_STATION)) try: for module_name in data.get_module_names(): for variable in \ diff --git a/homeassistant/components/wemo/binary_sensor.py b/homeassistant/components/wemo/binary_sensor.py index d6c1ad721b9abe..6606a5bd65dc77 100644 --- a/homeassistant/components/wemo/binary_sensor.py +++ b/homeassistant/components/wemo/binary_sensor.py @@ -8,6 +8,8 @@ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.exceptions import PlatformNotReady +from . import SUBSCRIPTION_REGISTRY + DEPENDENCIES = ['wemo'] _LOGGER = logging.getLogger(__name__) @@ -66,7 +68,7 @@ async def async_added_to_hass(self): # Define inside async context so we know our event loop self._update_lock = asyncio.Lock() - registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY + registry = SUBSCRIPTION_REGISTRY await self.hass.async_add_executor_job(registry.register, self.wemo) registry.on(self.wemo, None, self._subscription_callback) diff --git a/homeassistant/components/wemo/fan.py b/homeassistant/components/wemo/fan.py index 29a493bf5bc256..c5f3c0a16fa44f 100644 --- a/homeassistant/components/wemo/fan.py +++ b/homeassistant/components/wemo/fan.py @@ -14,6 +14,8 @@ from homeassistant.exceptions import PlatformNotReady from homeassistant.const import ATTR_ENTITY_ID +from . import SUBSCRIPTION_REGISTRY + DEPENDENCIES = ['wemo'] SCAN_INTERVAL = timedelta(seconds=10) DATA_KEY = 'fan.wemo' @@ -229,7 +231,7 @@ async def async_added_to_hass(self): # Define inside async context so we know our event loop self._update_lock = asyncio.Lock() - registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY + registry = SUBSCRIPTION_REGISTRY await self.hass.async_add_executor_job(registry.register, self.wemo) registry.on(self.wemo, None, self._subscription_callback) diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index e0f729fb165e8c..ff7185cbf34862 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -13,6 +13,8 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.util.color as color_util +from . import SUBSCRIPTION_REGISTRY + DEPENDENCIES = ['wemo'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) @@ -226,7 +228,7 @@ async def async_added_to_hass(self): # Define inside async context so we know our event loop self._update_lock = asyncio.Lock() - registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY + registry = SUBSCRIPTION_REGISTRY await self.hass.async_add_executor_job(registry.register, self.wemo) registry.on(self.wemo, None, self._subscription_callback) diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index 0a583e49e966a0..21d4cb64904aea 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -12,6 +12,8 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, STATE_STANDBY, STATE_UNKNOWN) +from . import SUBSCRIPTION_REGISTRY + DEPENDENCIES = ['wemo'] SCAN_INTERVAL = timedelta(seconds=10) @@ -198,7 +200,7 @@ async def async_added_to_hass(self): # Define inside async context so we know our event loop self._update_lock = asyncio.Lock() - registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY + registry = SUBSCRIPTION_REGISTRY await self.hass.async_add_job(registry.register, self.wemo) registry.on(self.wemo, None, self._subscription_callback) From 6c51592e345851c35c4ea12b390722f50db00620 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Fri, 12 Apr 2019 17:01:28 +1000 Subject: [PATCH 581/605] =?UTF-8?q?Instituto=20Geogr=C3=A1fico=20Nacional?= =?UTF-8?q?=20Sismolog=C3=ADa=20(Earthquakes)=20Feed=20platform=20(#22696)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * added new requirements * fixed tests * improved test coverage * added feed entry's title * added manifest * updated codeowners * generated requirements --- CODEOWNERS | 1 + .../components/ign_sismologia/__init__.py | 1 + .../components/ign_sismologia/geo_location.py | 232 ++++++++++++++++++ .../components/ign_sismologia/manifest.json | 12 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/ign_sismologia/__init__.py | 1 + .../ign_sismologia/test_geo_location.py | 191 ++++++++++++++ 9 files changed, 445 insertions(+) create mode 100644 homeassistant/components/ign_sismologia/__init__.py create mode 100644 homeassistant/components/ign_sismologia/geo_location.py create mode 100644 homeassistant/components/ign_sismologia/manifest.json create mode 100644 tests/components/ign_sismologia/__init__.py create mode 100644 tests/components/ign_sismologia/test_geo_location.py diff --git a/CODEOWNERS b/CODEOWNERS index 2a0548a4ded3b2..2b45acea2e1b47 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -102,6 +102,7 @@ homeassistant/components/http/* @home-assistant/core homeassistant/components/huawei_lte/* @scop homeassistant/components/huawei_router/* @abmantis homeassistant/components/hue/* @balloob +homeassistant/components/ign_sismologia/* @exxamalte homeassistant/components/influxdb/* @fabaff homeassistant/components/input_boolean/* @home-assistant/core homeassistant/components/input_datetime/* @home-assistant/core diff --git a/homeassistant/components/ign_sismologia/__init__.py b/homeassistant/components/ign_sismologia/__init__.py new file mode 100644 index 00000000000000..0f9f82f8632025 --- /dev/null +++ b/homeassistant/components/ign_sismologia/__init__.py @@ -0,0 +1 @@ +"""The ign_sismologia component.""" diff --git a/homeassistant/components/ign_sismologia/geo_location.py b/homeassistant/components/ign_sismologia/geo_location.py new file mode 100644 index 00000000000000..e2d9d6510bd518 --- /dev/null +++ b/homeassistant/components/ign_sismologia/geo_location.py @@ -0,0 +1,232 @@ +"""Support for IGN Sismologia (Earthquakes) Feeds.""" +from datetime import timedelta +import logging +from typing import Optional + +import voluptuous as vol + +from homeassistant.components.geo_location import ( + PLATFORM_SCHEMA, GeolocationEvent) +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE, + CONF_RADIUS, CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START) +from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, dispatcher_send) +from homeassistant.helpers.event import track_time_interval + +REQUIREMENTS = ['georss_ign_sismologia_client==0.2'] + +_LOGGER = logging.getLogger(__name__) + +ATTR_EXTERNAL_ID = 'external_id' +ATTR_IMAGE_URL = 'image_url' +ATTR_MAGNITUDE = 'magnitude' +ATTR_PUBLICATION_DATE = 'publication_date' +ATTR_REGION = 'region' +ATTR_TITLE = 'title' + +CONF_MINIMUM_MAGNITUDE = 'minimum_magnitude' + +DEFAULT_MINIMUM_MAGNITUDE = 0.0 +DEFAULT_RADIUS_IN_KM = 50.0 +DEFAULT_UNIT_OF_MEASUREMENT = 'km' + +SCAN_INTERVAL = timedelta(minutes=5) + +SIGNAL_DELETE_ENTITY = 'ign_sismologia_delete_{}' +SIGNAL_UPDATE_ENTITY = 'ign_sismologia_update_{}' + +SOURCE = 'ign_sismologia' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_LATITUDE): cv.latitude, + vol.Optional(CONF_LONGITUDE): cv.longitude, + vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS_IN_KM): vol.Coerce(float), + vol.Optional(CONF_MINIMUM_MAGNITUDE, default=DEFAULT_MINIMUM_MAGNITUDE): + vol.All(vol.Coerce(float), vol.Range(min=0)) +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the IGN Sismologia Feed platform.""" + scan_interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL) + coordinates = (config.get(CONF_LATITUDE, hass.config.latitude), + config.get(CONF_LONGITUDE, hass.config.longitude)) + radius_in_km = config[CONF_RADIUS] + minimum_magnitude = config[CONF_MINIMUM_MAGNITUDE] + # Initialize the entity manager. + feed = IgnSismologiaFeedEntityManager( + hass, add_entities, scan_interval, coordinates, radius_in_km, + minimum_magnitude) + + def start_feed_manager(event): + """Start feed manager.""" + feed.startup() + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_feed_manager) + + +class IgnSismologiaFeedEntityManager: + """Feed Entity Manager for IGN Sismologia GeoRSS feed.""" + + def __init__(self, hass, add_entities, scan_interval, coordinates, + radius_in_km, minimum_magnitude): + """Initialize the Feed Entity Manager.""" + from georss_ign_sismologia_client import IgnSismologiaFeedManager + + self._hass = hass + self._feed_manager = IgnSismologiaFeedManager( + self._generate_entity, self._update_entity, self._remove_entity, + coordinates, filter_radius=radius_in_km, + filter_minimum_magnitude=minimum_magnitude) + self._add_entities = add_entities + self._scan_interval = scan_interval + + def startup(self): + """Start up this manager.""" + self._feed_manager.update() + self._init_regular_updates() + + def _init_regular_updates(self): + """Schedule regular updates at the specified interval.""" + track_time_interval( + self._hass, lambda now: self._feed_manager.update(), + self._scan_interval) + + def get_entry(self, external_id): + """Get feed entry by external id.""" + return self._feed_manager.feed_entries.get(external_id) + + def _generate_entity(self, external_id): + """Generate new entity.""" + new_entity = IgnSismologiaLocationEvent(self, external_id) + # Add new entities to HA. + self._add_entities([new_entity], True) + + def _update_entity(self, external_id): + """Update entity.""" + dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY.format(external_id)) + + def _remove_entity(self, external_id): + """Remove entity.""" + dispatcher_send(self._hass, SIGNAL_DELETE_ENTITY.format(external_id)) + + +class IgnSismologiaLocationEvent(GeolocationEvent): + """This represents an external event with IGN Sismologia feed data.""" + + def __init__(self, feed_manager, external_id): + """Initialize entity with data from feed entry.""" + self._feed_manager = feed_manager + self._external_id = external_id + self._title = None + self._distance = None + self._latitude = None + self._longitude = None + self._attribution = None + self._region = None + self._magnitude = None + self._publication_date = None + self._image_url = None + self._remove_signal_delete = None + self._remove_signal_update = None + + async def async_added_to_hass(self): + """Call when entity is added to hass.""" + self._remove_signal_delete = async_dispatcher_connect( + self.hass, SIGNAL_DELETE_ENTITY.format(self._external_id), + self._delete_callback) + self._remove_signal_update = async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_ENTITY.format(self._external_id), + self._update_callback) + + @callback + def _delete_callback(self): + """Remove this entity.""" + self._remove_signal_delete() + self._remove_signal_update() + self.hass.async_create_task(self.async_remove()) + + @callback + def _update_callback(self): + """Call update method.""" + self.async_schedule_update_ha_state(True) + + @property + def should_poll(self): + """No polling needed for IGN Sismologia feed location events.""" + return False + + async def async_update(self): + """Update this entity from the data held in the feed manager.""" + _LOGGER.debug("Updating %s", self._external_id) + feed_entry = self._feed_manager.get_entry(self._external_id) + if feed_entry: + self._update_from_feed(feed_entry) + + def _update_from_feed(self, feed_entry): + """Update the internal state from the provided feed entry.""" + self._title = feed_entry.title + self._distance = feed_entry.distance_to_home + self._latitude = feed_entry.coordinates[0] + self._longitude = feed_entry.coordinates[1] + self._attribution = feed_entry.attribution + self._region = feed_entry.region + self._magnitude = feed_entry.magnitude + self._publication_date = feed_entry.published + self._image_url = feed_entry.image_url + + @property + def source(self) -> str: + """Return source value of this external event.""" + return SOURCE + + @property + def name(self) -> Optional[str]: + """Return the name of the entity.""" + if self._magnitude and self._region: + return "M {:.1f} - {}".format(self._magnitude, self._region) + if self._magnitude: + return "M {:.1f}".format(self._magnitude) + if self._region: + return self._region + return self._title + + @property + def distance(self) -> Optional[float]: + """Return distance value of this external event.""" + return self._distance + + @property + def latitude(self) -> Optional[float]: + """Return latitude value of this external event.""" + return self._latitude + + @property + def longitude(self) -> Optional[float]: + """Return longitude value of this external event.""" + return self._longitude + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return DEFAULT_UNIT_OF_MEASUREMENT + + @property + def device_state_attributes(self): + """Return the device state attributes.""" + attributes = {} + for key, value in ( + (ATTR_EXTERNAL_ID, self._external_id), + (ATTR_TITLE, self._title), + (ATTR_REGION, self._region), + (ATTR_MAGNITUDE, self._magnitude), + (ATTR_ATTRIBUTION, self._attribution), + (ATTR_PUBLICATION_DATE, self._publication_date), + (ATTR_IMAGE_URL, self._image_url) + ): + if value or isinstance(value, bool): + attributes[key] = value + return attributes diff --git a/homeassistant/components/ign_sismologia/manifest.json b/homeassistant/components/ign_sismologia/manifest.json new file mode 100644 index 00000000000000..d2ab3ad449cd11 --- /dev/null +++ b/homeassistant/components/ign_sismologia/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "ign_sismologia", + "name": "IGN Sismologia", + "documentation": "https://www.home-assistant.io/components/ign_sismologia", + "requirements": [ + "georss_ign_sismologia_client==0.2" + ], + "dependencies": [], + "codeowners": [ + "@exxamalte" + ] +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 5a1367f7d137a6..633a7150897681 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -466,6 +466,9 @@ geojson_client==0.3 # homeassistant.components.geo_rss_events georss_generic_client==0.2 +# homeassistant.components.ign_sismologia +georss_ign_sismologia_client==0.2 + # homeassistant.components.gitter gitterpy==0.1.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 25b4ecaf6d02f3..2abf5a43775371 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -111,6 +111,9 @@ geojson_client==0.3 # homeassistant.components.geo_rss_events georss_generic_client==0.2 +# homeassistant.components.ign_sismologia +georss_ign_sismologia_client==0.2 + # homeassistant.components.google google-api-python-client==1.6.4 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index e1179c904ce285..c8622837cf5dd9 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -62,6 +62,7 @@ 'foobot_async', 'geojson_client', 'georss_generic_client', + 'georss_ign_sismologia_client', 'google-api-python-client', 'gTTS-token', 'ha-ffmpeg', diff --git a/tests/components/ign_sismologia/__init__.py b/tests/components/ign_sismologia/__init__.py new file mode 100644 index 00000000000000..785f72013bd97a --- /dev/null +++ b/tests/components/ign_sismologia/__init__.py @@ -0,0 +1 @@ +"""Tests for the ign_sismologia component.""" diff --git a/tests/components/ign_sismologia/test_geo_location.py b/tests/components/ign_sismologia/test_geo_location.py new file mode 100644 index 00000000000000..3adddf3eea5fda --- /dev/null +++ b/tests/components/ign_sismologia/test_geo_location.py @@ -0,0 +1,191 @@ +"""The tests for the IGN Sismologia (Earthquakes) Feed platform.""" +import datetime +from unittest.mock import patch, MagicMock, call + +from homeassistant.components import geo_location +from homeassistant.components.geo_location import ATTR_SOURCE +from homeassistant.components.ign_sismologia.geo_location import ( + ATTR_EXTERNAL_ID, SCAN_INTERVAL, ATTR_REGION, + ATTR_MAGNITUDE, ATTR_IMAGE_URL, ATTR_PUBLICATION_DATE, ATTR_TITLE) +from homeassistant.const import EVENT_HOMEASSISTANT_START, \ + CONF_RADIUS, ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_FRIENDLY_NAME, \ + ATTR_UNIT_OF_MEASUREMENT, ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.setup import async_setup_component +from tests.common import assert_setup_component, async_fire_time_changed +import homeassistant.util.dt as dt_util + +CONFIG = { + geo_location.DOMAIN: [ + { + 'platform': 'ign_sismologia', + CONF_RADIUS: 200 + } + ] +} + +CONFIG_WITH_CUSTOM_LOCATION = { + geo_location.DOMAIN: [ + { + 'platform': 'ign_sismologia', + CONF_RADIUS: 200, + CONF_LATITUDE: 40.4, + CONF_LONGITUDE: -3.7 + } + ] +} + + +def _generate_mock_feed_entry(external_id, title, distance_to_home, + coordinates, region=None, + attribution=None, published=None, + magnitude=None, image_url=None): + """Construct a mock feed entry for testing purposes.""" + feed_entry = MagicMock() + feed_entry.external_id = external_id + feed_entry.title = title + feed_entry.distance_to_home = distance_to_home + feed_entry.coordinates = coordinates + feed_entry.region = region + feed_entry.attribution = attribution + feed_entry.published = published + feed_entry.magnitude = magnitude + feed_entry.image_url = image_url + return feed_entry + + +async def test_setup(hass): + """Test the general setup of the platform.""" + # Set up some mock feed entries for this test. + mock_entry_1 = _generate_mock_feed_entry( + '1234', 'Title 1', 15.5, (38.0, -3.0), + region='Region 1', attribution='Attribution 1', + published=datetime.datetime(2018, 9, 22, 8, 0, + tzinfo=datetime.timezone.utc), + magnitude=5.7, image_url='http://image.url/map.jpg') + mock_entry_2 = _generate_mock_feed_entry( + '2345', 'Title 2', 20.5, (38.1, -3.1), magnitude=4.6) + mock_entry_3 = _generate_mock_feed_entry( + '3456', 'Title 3', 25.5, (38.2, -3.2), region='Region 3') + mock_entry_4 = _generate_mock_feed_entry( + '4567', 'Title 4', 12.5, (38.3, -3.3)) + + # Patching 'utcnow' to gain more control over the timed update. + utcnow = dt_util.utcnow() + with patch('homeassistant.util.dt.utcnow', return_value=utcnow), \ + patch('georss_ign_sismologia_client.' + 'IgnSismologiaFeed') as mock_feed: + mock_feed.return_value.update.return_value = 'OK', [mock_entry_1, + mock_entry_2, + mock_entry_3] + with assert_setup_component(1, geo_location.DOMAIN): + assert await async_setup_component( + hass, geo_location.DOMAIN, CONFIG) + # Artificially trigger update. + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + # Collect events. + await hass.async_block_till_done() + + all_states = hass.states.async_all() + assert len(all_states) == 3 + + state = hass.states.get("geo_location.m_5_7_region_1") + assert state is not None + assert state.name == "M 5.7 - Region 1" + assert state.attributes == { + ATTR_EXTERNAL_ID: "1234", + ATTR_LATITUDE: 38.0, + ATTR_LONGITUDE: -3.0, + ATTR_FRIENDLY_NAME: "M 5.7 - Region 1", + ATTR_TITLE: "Title 1", + ATTR_REGION: "Region 1", + ATTR_ATTRIBUTION: "Attribution 1", + ATTR_PUBLICATION_DATE: + datetime.datetime( + 2018, 9, 22, 8, 0, tzinfo=datetime.timezone.utc), + ATTR_IMAGE_URL: 'http://image.url/map.jpg', + ATTR_MAGNITUDE: 5.7, + ATTR_UNIT_OF_MEASUREMENT: "km", + ATTR_SOURCE: 'ign_sismologia'} + assert float(state.state) == 15.5 + + state = hass.states.get("geo_location.m_4_6") + assert state is not None + assert state.name == "M 4.6" + assert state.attributes == { + ATTR_EXTERNAL_ID: "2345", + ATTR_LATITUDE: 38.1, + ATTR_LONGITUDE: -3.1, + ATTR_FRIENDLY_NAME: "M 4.6", + ATTR_TITLE: "Title 2", + ATTR_MAGNITUDE: 4.6, + ATTR_UNIT_OF_MEASUREMENT: "km", + ATTR_SOURCE: 'ign_sismologia'} + assert float(state.state) == 20.5 + + state = hass.states.get("geo_location.region_3") + assert state is not None + assert state.name == "Region 3" + assert state.attributes == { + ATTR_EXTERNAL_ID: "3456", + ATTR_LATITUDE: 38.2, + ATTR_LONGITUDE: -3.2, + ATTR_FRIENDLY_NAME: "Region 3", + ATTR_TITLE: "Title 3", + ATTR_REGION: "Region 3", + ATTR_UNIT_OF_MEASUREMENT: "km", + ATTR_SOURCE: 'ign_sismologia'} + assert float(state.state) == 25.5 + + # Simulate an update - one existing, one new entry, + # one outdated entry + mock_feed.return_value.update.return_value = 'OK', [ + mock_entry_1, mock_entry_4, mock_entry_3] + async_fire_time_changed(hass, utcnow + SCAN_INTERVAL) + await hass.async_block_till_done() + + all_states = hass.states.async_all() + assert len(all_states) == 3 + + # Simulate an update - empty data, but successful update, + # so no changes to entities. + mock_feed.return_value.update.return_value = 'OK_NO_DATA', None + async_fire_time_changed(hass, utcnow + 2 * SCAN_INTERVAL) + await hass.async_block_till_done() + + all_states = hass.states.async_all() + assert len(all_states) == 3 + + # Simulate an update - empty data, removes all entities + mock_feed.return_value.update.return_value = 'ERROR', None + async_fire_time_changed(hass, utcnow + 3 * SCAN_INTERVAL) + await hass.async_block_till_done() + + all_states = hass.states.async_all() + assert len(all_states) == 0 + + +async def test_setup_with_custom_location(hass): + """Test the setup with a custom location.""" + # Set up some mock feed entries for this test. + mock_entry_1 = _generate_mock_feed_entry( + '1234', 'Title 1', 20.5, (38.1, -3.1)) + + with patch('georss_ign_sismologia_client.' + 'IgnSismologiaFeed') as mock_feed: + mock_feed.return_value.update.return_value = 'OK', [mock_entry_1] + + with assert_setup_component(1, geo_location.DOMAIN): + assert await async_setup_component( + hass, geo_location.DOMAIN, CONFIG_WITH_CUSTOM_LOCATION) + + # Artificially trigger update. + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + # Collect events. + await hass.async_block_till_done() + + all_states = hass.states.async_all() + assert len(all_states) == 1 + + assert mock_feed.call_args == call( + (40.4, -3.7), filter_minimum_magnitude=0.0, + filter_radius=200.0) From 3d441dffad80a323936fac7313bf62ac5864253c Mon Sep 17 00:00:00 2001 From: Austin Drummond Date: Fri, 12 Apr 2019 07:26:52 -0400 Subject: [PATCH 582/605] Update HAP-python to 2.5.0 (#23031) --- homeassistant/components/homekit/__init__.py | 2 +- homeassistant/components/homekit/const.py | 2 +- homeassistant/components/homekit/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 01979f03b9a5f1..9d7de58be4bc5a 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -26,7 +26,7 @@ from .util import ( show_setup_message, validate_entity_config, validate_media_player_features) -REQUIREMENTS = ['HAP-python==2.4.2'] +REQUIREMENTS = ['HAP-python==2.5.0'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 4a96f0add8d92a..0a2b7a0fd5d889 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -110,8 +110,8 @@ CHAR_MOTION_DETECTED = 'MotionDetected' CHAR_NAME = 'Name' CHAR_OCCUPANCY_DETECTED = 'OccupancyDetected' -CHAR_OUTLET_IN_USE = 'OutletInUse' CHAR_ON = 'On' +CHAR_OUTLET_IN_USE = 'OutletInUse' CHAR_POSITION_STATE = 'PositionState' CHAR_ROTATION_DIRECTION = 'RotationDirection' CHAR_ROTATION_SPEED = 'RotationSpeed' diff --git a/homeassistant/components/homekit/manifest.json b/homeassistant/components/homekit/manifest.json index fd781f206d1609..e4aabfeb6cd958 100644 --- a/homeassistant/components/homekit/manifest.json +++ b/homeassistant/components/homekit/manifest.json @@ -3,7 +3,7 @@ "name": "Homekit", "documentation": "https://www.home-assistant.io/components/homekit", "requirements": [ - "HAP-python==2.4.2" + "HAP-python==2.5.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 633a7150897681..ee7503811c599a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -33,7 +33,7 @@ Adafruit-SHT31==1.0.2 # Adafruit_BBIO==1.0.0 # homeassistant.components.homekit -HAP-python==2.4.2 +HAP-python==2.5.0 # homeassistant.components.mastodon Mastodon.py==1.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2abf5a43775371..2977c426060bdd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -20,7 +20,7 @@ requests_mock==1.5.2 # homeassistant.components.homekit -HAP-python==2.4.2 +HAP-python==2.5.0 # homeassistant.components.mobile_app # homeassistant.components.owntracks From 7d46ed0bf9d09e901a79f894ff0a9d064fc17d80 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 12 Apr 2019 08:03:14 -0600 Subject: [PATCH 583/605] Remove expired 17track.net packages from entity registry (#23001) * Remove expired 17track.net packages from entity registry * Reverse order --- homeassistant/components/seventeentrack/sensor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/seventeentrack/sensor.py b/homeassistant/components/seventeentrack/sensor.py index ff17d1a4c546fc..8a242b7737da52 100644 --- a/homeassistant/components/seventeentrack/sensor.py +++ b/homeassistant/components/seventeentrack/sensor.py @@ -232,7 +232,8 @@ async def async_update(self): return # If the user has elected to not see delivered packages and one gets - # delivered, post a notification and delete the entity: + # delivered, post a notification, remove the entity from the UI, and + # delete it from the entity registry: if package.status == VALUE_DELIVERED and not self._data.show_delivered: _LOGGER.info('Package delivered: %s', self._tracking_number) self.hass.components.persistent_notification.create( @@ -245,6 +246,9 @@ async def async_update(self): title=NOTIFICATION_DELIVERED_TITLE, notification_id=NOTIFICATION_DELIVERED_ID_SCAFFOLD.format( self._tracking_number)) + + reg = self.hass.helpers.entity_registry.async_get_registry() + self.hass.async_create_task(reg.async_remove(self.entity_id)) self.hass.async_create_task(self.async_remove()) return From c8375be4b166112dcd809f07b36105a0509fe82a Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Fri, 12 Apr 2019 12:22:56 -0400 Subject: [PATCH 584/605] Replace get_platform (#23014) * Update Z-Wave to use async_get_integration * Change load method per PR comments * update tests --- homeassistant/components/zwave/__init__.py | 6 ++- tests/components/zwave/test_init.py | 47 +++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 6028e5547c6c79..2d575d99647d3b 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -1,6 +1,7 @@ """Support for Z-Wave.""" import asyncio import copy +from importlib import import_module import logging from pprint import pprint @@ -8,7 +9,6 @@ from homeassistant import config_entries from homeassistant.core import callback, 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 @@ -908,7 +908,9 @@ def _check_entity_ready(self): if polling_intensity: self.primary.enable_poll(polling_intensity) - platform = get_platform(self._hass, component, DOMAIN) + platform = import_module('.{}'.format(component), + __name__) + device = platform.get_device( node=self._node, values=self, node_config=node_config, hass=self._hass) diff --git a/tests/components/zwave/test_init.py b/tests/components/zwave/test_init.py index 3f0c082591cef8..f2f32aeb54c08b 100644 --- a/tests/components/zwave/test_init.py +++ b/tests/components/zwave/test_init.py @@ -558,13 +558,13 @@ def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" self.hass.stop() - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_discovery(self, discovery, get_platform): + def test_entity_discovery(self, discovery, import_module): """Test the creation of a new entity.""" discovery.async_load_platform.return_value = mock_coro() mock_platform = MagicMock() - get_platform.return_value = mock_platform + import_module.return_value = mock_platform mock_device = MagicMock() mock_device.name = 'test_device' mock_platform.get_device.return_value = mock_device @@ -618,13 +618,13 @@ def test_entity_discovery(self, discovery, get_platform): assert values._entity.value_changed.called assert len(values._entity.value_changed.mock_calls) == 1 - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_existing_values(self, discovery, get_platform): + def test_entity_existing_values(self, discovery, import_module): """Test the loading of already discovered values.""" discovery.async_load_platform.return_value = mock_coro() mock_platform = MagicMock() - get_platform.return_value = mock_platform + import_module.return_value = mock_platform mock_device = MagicMock() mock_device.name = 'test_device' mock_platform.get_device.return_value = mock_device @@ -663,9 +663,9 @@ def test_entity_existing_values(self, discovery, get_platform): assert args[4] == self.zwave_config assert not self.primary.enable_poll.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_node_schema_mismatch(self, discovery, get_platform): + def test_node_schema_mismatch(self, discovery, import_module): """Test node schema mismatch.""" self.node.generic = 'no_match' self.node.values = { @@ -686,13 +686,13 @@ def test_node_schema_mismatch(self, discovery, get_platform): assert not discovery.async_load_platform.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_workaround_component(self, discovery, get_platform): + def test_entity_workaround_component(self, discovery, import_module): """Test component workaround.""" discovery.async_load_platform.return_value = mock_coro() mock_platform = MagicMock() - get_platform.return_value = mock_platform + import_module.return_value = mock_platform mock_device = MagicMock() mock_device.name = 'test_device' mock_platform.get_device.return_value = mock_device @@ -729,9 +729,9 @@ def test_entity_workaround_component(self, discovery, get_platform): args = mock_dispatch_send.mock_calls[0][1] assert args[1] == 'zwave_new_binary_sensor' - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_workaround_ignore(self, discovery, get_platform): + def test_entity_workaround_ignore(self, discovery, import_module): """Test ignore workaround.""" self.node.manufacturer_id = '010f' self.node.product_type = '0301' @@ -758,9 +758,9 @@ def test_entity_workaround_ignore(self, discovery, get_platform): assert not discovery.async_load_platform.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_config_ignore(self, discovery, get_platform): + def test_entity_config_ignore(self, discovery, import_module): """Test ignore config.""" self.node.values = { self.primary.value_id: self.primary, @@ -782,9 +782,10 @@ def test_entity_config_ignore(self, discovery, get_platform): assert not discovery.async_load_platform.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_config_ignore_with_registry(self, discovery, get_platform): + def test_entity_config_ignore_with_registry(self, discovery, + import_module): """Test ignore config. The case when the device is in entity registry. @@ -813,16 +814,16 @@ def test_entity_config_ignore_with_registry(self, discovery, get_platform): assert not discovery.async_load_platform.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_entity_platform_ignore(self, discovery, get_platform): + def test_entity_platform_ignore(self, discovery, import_module): """Test platform ignore device.""" self.node.values = { self.primary.value_id: self.primary, self.secondary.value_id: self.secondary, } platform = MagicMock() - get_platform.return_value = platform + import_module.return_value = platform platform.get_device.return_value = None zwave.ZWaveDeviceEntityValues( hass=self.hass, @@ -836,12 +837,12 @@ def test_entity_platform_ignore(self, discovery, get_platform): assert not discovery.async_load_platform.called - @patch.object(zwave, 'get_platform') + @patch.object(zwave, 'import_module') @patch.object(zwave, 'discovery') - def test_config_polling_intensity(self, discovery, get_platform): + def test_config_polling_intensity(self, discovery, import_module): """Test polling intensity.""" mock_platform = MagicMock() - get_platform.return_value = mock_platform + import_module.return_value = mock_platform mock_device = MagicMock() mock_device.name = 'test_device' mock_platform.get_device.return_value = mock_device From f7d4c48199d8333d9d9397a992a611d7a188f2c4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 12 Apr 2019 10:09:17 -0700 Subject: [PATCH 585/605] Convert service helper to use async_get_integration (#23023) * Convert service helper to use async_get_integration * Fix tests --- homeassistant/helpers/service.py | 75 ++++++++++++++++++-------------- homeassistant/loader.py | 10 +++-- tests/common.py | 2 +- tests/test_loader.py | 13 +++--- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index ea62d12c66c02e..f5de2419fd4416 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -2,7 +2,6 @@ import asyncio from functools import wraps import logging -from os import path from typing import Callable import voluptuous as vol @@ -11,12 +10,14 @@ from homeassistant.const import ( ATTR_ENTITY_ID, ENTITY_MATCH_ALL, ATTR_AREA_ID) import homeassistant.core as ha -from homeassistant.exceptions import TemplateError, Unauthorized, UnknownUser +from homeassistant.exceptions import ( + HomeAssistantError, TemplateError, Unauthorized, UnknownUser) from homeassistant.helpers import template, typing -from homeassistant.loader import get_component, bind_hass +from homeassistant.loader import async_get_integration, bind_hass from homeassistant.util.yaml import load_yaml import homeassistant.helpers.config_validation as cv from homeassistant.util.async_ import run_coroutine_threadsafe +from homeassistant.helpers.typing import HomeAssistantType CONF_SERVICE = 'service' CONF_SERVICE_TEMPLATE = 'service_template' @@ -152,60 +153,68 @@ async def async_extract_entity_ids(hass, service_call, expand_group=True): return extracted +async def _load_services_file(hass: HomeAssistantType, domain: str): + """Load services file for an integration.""" + integration = await async_get_integration(hass, domain) + try: + return await hass.async_add_executor_job( + load_yaml, str(integration.file_path / 'services.yaml')) + except FileNotFoundError: + _LOGGER.warning("Unable to find services.yaml for the %s integration", + domain) + return {} + except HomeAssistantError: + _LOGGER.warning("Unable to parse services.yaml for the %s integration", + domain) + return {} + + @bind_hass async def async_get_all_descriptions(hass): """Return descriptions (i.e. user documentation) for all service calls.""" - if SERVICE_DESCRIPTION_CACHE not in hass.data: - hass.data[SERVICE_DESCRIPTION_CACHE] = {} - description_cache = hass.data[SERVICE_DESCRIPTION_CACHE] - + descriptions_cache = hass.data.setdefault(SERVICE_DESCRIPTION_CACHE, {}) format_cache_key = '{}.{}'.format - - def domain_yaml_file(domain): - """Return the services.yaml location for a domain.""" - component_path = path.dirname(get_component(hass, domain).__file__) - return path.join(component_path, 'services.yaml') - - def load_services_files(yaml_files): - """Load and parse services.yaml files.""" - loaded = {} - for yaml_file in yaml_files: - try: - loaded[yaml_file] = load_yaml(yaml_file) - except FileNotFoundError: - loaded[yaml_file] = {} - - return loaded - services = hass.services.async_services() - # Load missing files + # See if there are new services not seen before. + # Any service that we saw before already has an entry in description_cache. missing = set() for domain in services: for service in services[domain]: - if format_cache_key(domain, service) not in description_cache: - missing.add(domain_yaml_file(domain)) + if format_cache_key(domain, service) not in descriptions_cache: + missing.add(domain) break + # Files we loaded for missing descriptions + loaded = {} + if missing: - loaded = await hass.async_add_job(load_services_files, missing) + contents = await asyncio.gather(*[ + _load_services_file(hass, domain) for domain in missing + ]) + + for domain, content in zip(missing, contents): + loaded[domain] = content # Build response descriptions = {} for domain in services: descriptions[domain] = {} - yaml_file = domain_yaml_file(domain) for service in services[domain]: cache_key = format_cache_key(domain, service) - description = description_cache.get(cache_key) + description = descriptions_cache.get(cache_key) # Cache missing descriptions if description is None: - yaml_services = loaded[yaml_file] - yaml_description = yaml_services.get(service, {}) + domain_yaml = loaded[domain] + yaml_description = domain_yaml.get(service, {}) + + if not yaml_description: + _LOGGER.warning("Missing service description for %s/%s", + domain, service) - description = description_cache[cache_key] = { + description = descriptions_cache[cache_key] = { 'description': yaml_description.get('description', ''), 'fields': yaml_description.get('fields', {}) } diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 0b7495bcb6941b..44e5ab23d78ca7 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -87,7 +87,8 @@ def resolve_from_root(cls, hass: 'HomeAssistant', root_module: ModuleType, continue return cls( - hass, "{}.{}".format(root_module.__name__, domain), manifest + hass, "{}.{}".format(root_module.__name__, domain), + manifest_path.parent, manifest ) return None @@ -105,13 +106,16 @@ def resolve_legacy(cls, hass: 'HomeAssistant', domain: str) \ return None return cls( - hass, comp.__name__, manifest_from_legacy_module(comp) + hass, comp.__name__, pathlib.Path(comp.__file__).parent, + manifest_from_legacy_module(comp) ) - def __init__(self, hass: 'HomeAssistant', pkg_path: str, manifest: Dict): + def __init__(self, hass: 'HomeAssistant', pkg_path: str, + file_path: pathlib.Path, manifest: Dict): """Initialize an integration.""" self.hass = hass self.pkg_path = pkg_path + self.file_path = file_path self.name = manifest['name'] # type: str self.domain = manifest['domain'] # type: str self.dependencies = manifest['dependencies'] # type: List[str] diff --git a/tests/common.py b/tests/common.py index 4aa13fc9be6ece..255ceacedfbbf5 100644 --- a/tests/common.py +++ b/tests/common.py @@ -904,7 +904,7 @@ async def get_system_health_info(hass, domain): def mock_integration(hass, module): """Mock an integration.""" integration = loader.Integration( - hass, 'homeassisant.components.{}'.format(module.DOMAIN), + hass, 'homeassisant.components.{}'.format(module.DOMAIN), None, loader.manifest_from_legacy_module(module)) _LOGGER.info("Adding mock integration: %s", module.DOMAIN) diff --git a/tests/test_loader.py b/tests/test_loader.py index 9598906a82ba17..a70a34651eb4cb 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -164,12 +164,13 @@ async def test_get_integration_custom_component(hass): def test_integration_properties(hass): """Test integration properties.""" - integration = loader.Integration(hass, 'homeassistant.components.hue', { - 'name': 'Philips Hue', - 'domain': 'hue', - 'dependencies': ['test-dep'], - 'requirements': ['test-req==1.0.0'], - }) + integration = loader.Integration( + hass, 'homeassistant.components.hue', None, { + 'name': 'Philips Hue', + 'domain': 'hue', + 'dependencies': ['test-dep'], + 'requirements': ['test-req==1.0.0'], + }) assert integration.name == "Philips Hue" assert integration.domain == 'hue' assert integration.dependencies == ['test-dep'] From 2c07bfb9e0ac03fdf74b83152f4511ed7f7104b0 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Fri, 12 Apr 2019 19:13:30 +0200 Subject: [PATCH 586/605] Remove dependencies and requirements (#23024) * Remove dependencies and requirements * Revert "Remove dependencies and requirements" This reverts commit fe7171b4cd30889bad5adc9a4fd60059d05ba5a7. * Remove dependencies and requirements * Revert "Remove dependencies and requirements" This reverts commit 391355ee2cc53cbe6954f940062b18ae34b05621. * Remove dependencies and requirements * Fix flake8 complaints * Fix more flake8 complaints * Revert non-component removals --- homeassistant/components/abode/__init__.py | 2 -- .../components/abode/alarm_control_panel.py | 2 -- .../components/abode/binary_sensor.py | 2 -- homeassistant/components/abode/camera.py | 2 -- homeassistant/components/abode/cover.py | 2 -- homeassistant/components/abode/light.py | 2 -- homeassistant/components/abode/lock.py | 2 -- homeassistant/components/abode/sensor.py | 2 -- homeassistant/components/abode/switch.py | 2 -- .../components/acer_projector/switch.py | 2 -- homeassistant/components/ads/__init__.py | 2 -- homeassistant/components/ads/binary_sensor.py | 2 -- homeassistant/components/ads/light.py | 1 - homeassistant/components/ads/sensor.py | 2 -- homeassistant/components/ads/switch.py | 2 -- homeassistant/components/aftership/sensor.py | 2 -- homeassistant/components/airvisual/sensor.py | 1 - .../components/aladdin_connect/cover.py | 2 -- .../components/alarmdecoder/__init__.py | 2 -- .../alarmdecoder/alarm_control_panel.py | 2 -- .../components/alarmdecoder/binary_sensor.py | 2 -- .../components/alarmdecoder/sensor.py | 2 -- .../alarmdotcom/alarm_control_panel.py | 2 -- homeassistant/components/alexa/__init__.py | 2 -- .../components/alpha_vantage/sensor.py | 2 -- homeassistant/components/amazon_polly/tts.py | 2 -- .../components/ambient_station/__init__.py | 2 -- .../ambient_station/binary_sensor.py | 2 -- .../components/ambient_station/sensor.py | 2 -- homeassistant/components/amcrest/__init__.py | 3 --- .../components/amcrest/binary_sensor.py | 2 -- homeassistant/components/amcrest/camera.py | 2 -- homeassistant/components/amcrest/sensor.py | 2 -- homeassistant/components/amcrest/switch.py | 2 -- homeassistant/components/ampio/air_quality.py | 2 -- .../components/android_ip_webcam/__init__.py | 2 -- .../android_ip_webcam/binary_sensor.py | 2 -- .../components/android_ip_webcam/sensor.py | 2 -- .../components/android_ip_webcam/switch.py | 2 -- .../components/androidtv/media_player.py | 2 -- .../components/anel_pwrctrl/switch.py | 2 -- .../components/anthemav/media_player.py | 2 -- homeassistant/components/apcupsd/__init__.py | 2 -- .../components/apcupsd/binary_sensor.py | 2 -- homeassistant/components/apcupsd/sensor.py | 2 -- homeassistant/components/api/__init__.py | 2 -- homeassistant/components/apns/notify.py | 2 -- homeassistant/components/apple_tv/__init__.py | 2 -- .../components/apple_tv/media_player.py | 2 -- homeassistant/components/apple_tv/remote.py | 2 -- .../components/aqualogic/__init__.py | 2 -- homeassistant/components/aqualogic/sensor.py | 2 -- homeassistant/components/aqualogic/switch.py | 2 -- .../components/aquostv/media_player.py | 2 -- homeassistant/components/arduino/__init__.py | 2 -- homeassistant/components/arduino/sensor.py | 2 -- homeassistant/components/arduino/switch.py | 2 -- homeassistant/components/arlo/__init__.py | 2 -- .../components/arlo/alarm_control_panel.py | 2 -- homeassistant/components/arlo/camera.py | 2 -- homeassistant/components/arlo/sensor.py | 2 -- .../components/aruba/device_tracker.py | 2 -- homeassistant/components/arwn/sensor.py | 1 - .../components/asterisk_cdr/mailbox.py | 2 -- .../components/asterisk_mbox/__init__.py | 2 -- .../components/asterisk_mbox/mailbox.py | 2 -- homeassistant/components/asuswrt/__init__.py | 2 -- .../components/asuswrt/device_tracker.py | 2 -- homeassistant/components/asuswrt/sensor.py | 2 -- homeassistant/components/august/__init__.py | 2 -- .../components/august/binary_sensor.py | 2 -- homeassistant/components/august/camera.py | 2 -- homeassistant/components/august/lock.py | 2 -- homeassistant/components/auth/__init__.py | 2 -- .../components/automatic/device_tracker.py | 4 ---- .../components/automation/__init__.py | 1 - .../components/automation/litejet.py | 2 -- homeassistant/components/automation/mqtt.py | 2 -- homeassistant/components/avion/light.py | 2 -- homeassistant/components/awair/sensor.py | 2 -- homeassistant/components/aws/__init__.py | 2 -- homeassistant/components/aws/notify.py | 2 -- homeassistant/components/axis/__init__.py | 2 -- .../components/axis/binary_sensor.py | 2 -- homeassistant/components/axis/camera.py | 2 -- homeassistant/components/baidu/tts.py | 2 -- homeassistant/components/bbb_gpio/__init__.py | 2 -- .../components/bbb_gpio/binary_sensor.py | 2 -- homeassistant/components/bbb_gpio/switch.py | 2 -- .../components/bbox/device_tracker.py | 2 -- homeassistant/components/bbox/sensor.py | 2 -- homeassistant/components/bh1750/sensor.py | 3 --- homeassistant/components/bitcoin/sensor.py | 2 -- .../components/blackbird/media_player.py | 2 -- homeassistant/components/blink/__init__.py | 2 -- .../components/blink/alarm_control_panel.py | 2 -- .../components/blink/binary_sensor.py | 2 -- homeassistant/components/blink/camera.py | 2 -- homeassistant/components/blink/sensor.py | 2 -- .../components/blinksticklight/light.py | 2 -- homeassistant/components/blinkt/light.py | 2 -- homeassistant/components/blockchain/sensor.py | 2 -- .../components/bloomsky/binary_sensor.py | 2 -- homeassistant/components/bloomsky/camera.py | 2 -- homeassistant/components/bloomsky/sensor.py | 2 -- .../components/bluesound/media_player.py | 2 -- .../bluetooth_le_tracker/device_tracker.py | 2 -- .../bluetooth_tracker/device_tracker.py | 2 -- homeassistant/components/bme280/sensor.py | 3 --- homeassistant/components/bme680/sensor.py | 3 --- .../bmw_connected_drive/__init__.py | 2 -- .../bmw_connected_drive/binary_sensor.py | 2 -- .../bmw_connected_drive/device_tracker.py | 2 -- .../components/bmw_connected_drive/lock.py | 2 -- .../components/bmw_connected_drive/sensor.py | 2 -- homeassistant/components/bom/camera.py | 2 -- .../components/braviatv/media_player.py | 2 -- homeassistant/components/broadlink/sensor.py | 2 -- homeassistant/components/broadlink/switch.py | 2 -- .../components/brottsplatskartan/sensor.py | 2 -- homeassistant/components/brunt/cover.py | 2 -- .../bt_home_hub_5/device_tracker.py | 2 -- .../components/bt_smarthub/device_tracker.py | 2 -- homeassistant/components/buienradar/sensor.py | 2 -- .../components/buienradar/weather.py | 2 -- homeassistant/components/caldav/calendar.py | 2 -- homeassistant/components/calendar/__init__.py | 2 -- homeassistant/components/camera/__init__.py | 2 -- homeassistant/components/canary/__init__.py | 2 -- .../components/canary/alarm_control_panel.py | 2 -- homeassistant/components/canary/camera.py | 2 -- homeassistant/components/canary/sensor.py | 2 -- homeassistant/components/cast/__init__.py | 2 -- .../components/channels/media_player.py | 2 -- .../components/cisco_ios/device_tracker.py | 2 -- .../cisco_mobility_express/device_tracker.py | 4 ---- .../components/cisco_webex_teams/notify.py | 2 -- homeassistant/components/ciscospark/notify.py | 2 -- .../components/clementine/media_player.py | 2 -- homeassistant/components/cloud/__init__.py | 3 --- .../components/cloud/binary_sensor.py | 2 -- .../components/cloudflare/__init__.py | 2 -- homeassistant/components/cmus/media_player.py | 2 -- homeassistant/components/co2signal/sensor.py | 2 -- homeassistant/components/coinbase/__init__.py | 2 -- homeassistant/components/coinbase/sensor.py | 1 - .../components/coinmarketcap/sensor.py | 2 -- .../components/comfoconnect/__init__.py | 2 -- homeassistant/components/comfoconnect/fan.py | 2 -- .../components/comfoconnect/sensor.py | 2 -- .../concord232/alarm_control_panel.py | 2 -- .../components/concord232/binary_sensor.py | 2 -- homeassistant/components/config/__init__.py | 1 - .../components/conversation/__init__.py | 1 - .../components/coolmaster/climate.py | 2 -- homeassistant/components/cover/__init__.py | 1 - .../components/cppm_tracker/device_tracker.py | 2 -- homeassistant/components/cpuspeed/sensor.py | 2 -- .../components/crimereports/sensor.py | 2 -- homeassistant/components/cups/sensor.py | 2 -- homeassistant/components/daikin/__init__.py | 2 -- .../components/danfoss_air/__init__.py | 2 -- homeassistant/components/darksky/sensor.py | 2 -- homeassistant/components/darksky/weather.py | 2 -- homeassistant/components/datadog/__init__.py | 2 -- homeassistant/components/deconz/__init__.py | 2 -- .../components/deconz/binary_sensor.py | 2 -- homeassistant/components/deconz/climate.py | 2 -- homeassistant/components/deconz/cover.py | 2 -- homeassistant/components/deconz/light.py | 2 -- homeassistant/components/deconz/scene.py | 2 -- homeassistant/components/deconz/sensor.py | 2 -- homeassistant/components/deconz/switch.py | 2 -- homeassistant/components/decora/light.py | 2 -- homeassistant/components/decora_wifi/light.py | 2 -- .../components/default_config/__init__.py | 20 ------------------- homeassistant/components/deluge/sensor.py | 2 -- homeassistant/components/deluge/switch.py | 2 -- homeassistant/components/demo/__init__.py | 1 - .../components/denonavr/media_player.py | 2 -- .../components/deutsche_bahn/sensor.py | 2 -- .../device_sun_light_trigger/__init__.py | 2 -- .../components/device_tracker/__init__.py | 2 -- homeassistant/components/dht/sensor.py | 2 -- .../components/dialogflow/__init__.py | 1 - .../components/digital_ocean/__init__.py | 2 -- .../components/digital_ocean/binary_sensor.py | 2 -- .../components/digital_ocean/switch.py | 2 -- .../components/digitalloggers/switch.py | 2 -- .../components/directv/media_player.py | 2 -- homeassistant/components/discogs/sensor.py | 2 -- homeassistant/components/discord/notify.py | 2 -- .../components/discovery/__init__.py | 2 -- .../dlib_face_detect/image_processing.py | 2 -- .../dlib_face_identify/image_processing.py | 2 -- homeassistant/components/dlink/switch.py | 2 -- .../components/dlna_dmr/media_player.py | 2 -- homeassistant/components/dnsip/sensor.py | 2 -- homeassistant/components/dominos/__init__.py | 4 ---- homeassistant/components/doorbird/__init__.py | 2 -- homeassistant/components/doorbird/camera.py | 2 -- homeassistant/components/doorbird/switch.py | 2 -- homeassistant/components/dovado/__init__.py | 2 -- homeassistant/components/dovado/notify.py | 2 -- homeassistant/components/dovado/sensor.py | 2 -- homeassistant/components/dsmr/sensor.py | 2 -- .../components/duke_energy/sensor.py | 2 -- .../components/dunehd/media_player.py | 2 -- homeassistant/components/dweet/__init__.py | 2 -- homeassistant/components/dweet/sensor.py | 2 -- homeassistant/components/dyson/__init__.py | 2 -- homeassistant/components/dyson/fan.py | 1 - homeassistant/components/dyson/sensor.py | 2 -- homeassistant/components/dyson/vacuum.py | 2 -- homeassistant/components/ebox/sensor.py | 2 -- homeassistant/components/ebusd/__init__.py | 2 -- homeassistant/components/ebusd/sensor.py | 2 -- .../components/ecoal_boiler/__init__.py | 2 -- .../components/ecoal_boiler/sensor.py | 2 -- .../components/ecoal_boiler/switch.py | 2 -- homeassistant/components/ecobee/__init__.py | 2 -- .../components/ecobee/binary_sensor.py | 2 -- homeassistant/components/ecobee/climate.py | 2 -- homeassistant/components/ecobee/notify.py | 2 -- homeassistant/components/ecobee/sensor.py | 2 -- homeassistant/components/ecobee/weather.py | 2 -- .../components/econet/water_heater.py | 2 -- homeassistant/components/ecovacs/__init__.py | 2 -- homeassistant/components/ecovacs/vacuum.py | 2 -- .../eddystone_temperature/sensor.py | 2 -- homeassistant/components/edimax/switch.py | 2 -- homeassistant/components/edp_redy/__init__.py | 2 -- homeassistant/components/edp_redy/sensor.py | 2 -- homeassistant/components/edp_redy/switch.py | 2 -- .../components/ee_brightbox/device_tracker.py | 2 -- homeassistant/components/egardia/__init__.py | 2 -- .../components/egardia/alarm_control_panel.py | 2 -- .../components/egardia/binary_sensor.py | 2 -- .../components/eight_sleep/__init__.py | 2 -- .../components/eight_sleep/binary_sensor.py | 2 -- .../components/eight_sleep/sensor.py | 2 -- homeassistant/components/eliqonline/sensor.py | 2 -- homeassistant/components/elkm1/__init__.py | 2 -- .../components/elkm1/alarm_control_panel.py | 2 -- homeassistant/components/elkm1/climate.py | 2 -- homeassistant/components/elkm1/light.py | 2 -- homeassistant/components/elkm1/scene.py | 2 -- homeassistant/components/elkm1/sensor.py | 2 -- homeassistant/components/elkm1/switch.py | 2 -- homeassistant/components/emby/media_player.py | 2 -- .../components/emulated_hue/__init__.py | 1 - .../components/emulated_roku/__init__.py | 2 -- .../components/enigma2/media_player.py | 2 -- homeassistant/components/enocean/__init__.py | 2 -- .../components/enocean/binary_sensor.py | 1 - homeassistant/components/enocean/light.py | 2 -- homeassistant/components/enocean/sensor.py | 2 -- homeassistant/components/enocean/switch.py | 1 - .../components/enphase_envoy/sensor.py | 1 - .../entur_public_transport/sensor.py | 2 -- homeassistant/components/envirophat/sensor.py | 3 --- .../components/envisalink/__init__.py | 2 -- .../envisalink/alarm_control_panel.py | 2 -- .../components/envisalink/binary_sensor.py | 2 -- homeassistant/components/envisalink/sensor.py | 2 -- homeassistant/components/ephember/climate.py | 2 -- .../components/epson/media_player.py | 2 -- .../components/eq3btsmart/climate.py | 2 -- homeassistant/components/esphome/__init__.py | 2 -- .../components/esphome/binary_sensor.py | 1 - homeassistant/components/esphome/camera.py | 1 - homeassistant/components/esphome/climate.py | 1 - homeassistant/components/esphome/cover.py | 1 - homeassistant/components/esphome/fan.py | 1 - homeassistant/components/esphome/light.py | 1 - homeassistant/components/esphome/sensor.py | 1 - homeassistant/components/esphome/switch.py | 1 - homeassistant/components/etherscan/sensor.py | 2 -- homeassistant/components/eufy/__init__.py | 2 -- homeassistant/components/eufy/light.py | 2 -- homeassistant/components/eufy/switch.py | 2 -- homeassistant/components/everlights/light.py | 2 -- homeassistant/components/evohome/__init__.py | 2 -- homeassistant/components/familyhub/camera.py | 2 -- homeassistant/components/fan/__init__.py | 1 - .../components/fastdotcom/__init__.py | 2 -- homeassistant/components/fastdotcom/sensor.py | 2 -- homeassistant/components/fedex/sensor.py | 2 -- .../components/feedreader/__init__.py | 2 -- homeassistant/components/ffmpeg/__init__.py | 2 -- homeassistant/components/ffmpeg/camera.py | 2 -- .../components/ffmpeg_motion/binary_sensor.py | 2 -- .../components/ffmpeg_noise/binary_sensor.py | 2 -- homeassistant/components/fibaro/__init__.py | 2 -- .../components/fibaro/binary_sensor.py | 2 -- homeassistant/components/fibaro/climate.py | 2 -- homeassistant/components/fibaro/cover.py | 2 -- homeassistant/components/fibaro/light.py | 2 -- homeassistant/components/fibaro/scene.py | 2 -- homeassistant/components/fibaro/sensor.py | 1 - homeassistant/components/fibaro/switch.py | 1 - homeassistant/components/fido/sensor.py | 2 -- homeassistant/components/fints/sensor.py | 2 -- homeassistant/components/fitbit/sensor.py | 4 ---- homeassistant/components/fixer/sensor.py | 2 -- homeassistant/components/flexit/climate.py | 3 --- .../components/flic/binary_sensor.py | 2 -- homeassistant/components/flunearyou/sensor.py | 1 - homeassistant/components/flux/switch.py | 2 -- homeassistant/components/flux_led/light.py | 2 -- .../components/folder_watcher/__init__.py | 2 -- homeassistant/components/foobot/sensor.py | 2 -- homeassistant/components/foscam/camera.py | 2 -- .../components/foursquare/__init__.py | 1 - .../components/free_mobile/notify.py | 2 -- homeassistant/components/freebox/__init__.py | 2 -- .../components/freebox/device_tracker.py | 2 -- homeassistant/components/freebox/sensor.py | 2 -- homeassistant/components/freebox/switch.py | 2 -- .../components/fritz/device_tracker.py | 2 -- homeassistant/components/fritzbox/__init__.py | 2 -- .../components/fritzbox/binary_sensor.py | 2 -- homeassistant/components/fritzbox/climate.py | 2 -- homeassistant/components/fritzbox/sensor.py | 2 -- homeassistant/components/fritzbox/switch.py | 2 -- .../components/fritzbox_callmonitor/sensor.py | 2 -- .../components/fritzbox_netmonitor/sensor.py | 2 -- homeassistant/components/fritzdect/switch.py | 2 -- homeassistant/components/frontend/__init__.py | 5 ----- .../frontier_silicon/media_player.py | 2 -- homeassistant/components/futurenow/light.py | 2 -- homeassistant/components/gc100/__init__.py | 2 -- .../components/gc100/binary_sensor.py | 2 -- homeassistant/components/gc100/switch.py | 2 -- homeassistant/components/gearbest/sensor.py | 1 - homeassistant/components/geizhals/sensor.py | 2 -- .../components/generic_thermostat/climate.py | 2 -- .../geo_json_events/geo_location.py | 2 -- .../components/geo_rss_events/sensor.py | 2 -- homeassistant/components/geofency/__init__.py | 2 -- .../components/geofency/device_tracker.py | 2 -- homeassistant/components/github/sensor.py | 2 -- homeassistant/components/gitlab_ci/sensor.py | 2 -- homeassistant/components/gitter/sensor.py | 2 -- homeassistant/components/glances/sensor.py | 2 -- homeassistant/components/gntp/notify.py | 2 -- homeassistant/components/goalfeed/__init__.py | 1 - homeassistant/components/gogogate2/cover.py | 2 -- homeassistant/components/google/__init__.py | 6 ------ homeassistant/components/google/tts.py | 2 -- .../components/google_assistant/__init__.py | 2 -- .../components/google_maps/device_tracker.py | 2 -- .../components/google_pubsub/__init__.py | 2 -- .../components/google_travel_time/sensor.py | 2 -- .../components/googlehome/__init__.py | 2 -- .../components/googlehome/device_tracker.py | 2 -- homeassistant/components/googlehome/sensor.py | 2 -- .../components/gpmdp/media_player.py | 2 -- homeassistant/components/gpsd/sensor.py | 2 -- .../components/gpslogger/__init__.py | 2 -- .../components/gpslogger/device_tracker.py | 2 -- .../components/greeneye_monitor/__init__.py | 2 -- .../components/greeneye_monitor/sensor.py | 2 -- homeassistant/components/greenwave/light.py | 1 - .../components/gstreamer/media_player.py | 2 -- homeassistant/components/gtfs/sensor.py | 2 -- homeassistant/components/gtt/sensor.py | 2 -- homeassistant/components/habitica/__init__.py | 2 -- homeassistant/components/hangouts/__init__.py | 2 -- homeassistant/components/hangouts/notify.py | 2 -- .../harman_kardon_avr/media_player.py | 2 -- homeassistant/components/harmony/remote.py | 2 -- homeassistant/components/hassio/__init__.py | 1 - homeassistant/components/hdmi_cec/__init__.py | 2 -- .../components/hdmi_cec/media_player.py | 2 -- homeassistant/components/hdmi_cec/switch.py | 2 -- homeassistant/components/heatmiser/climate.py | 2 -- homeassistant/components/heos/__init__.py | 2 -- homeassistant/components/heos/media_player.py | 2 -- .../components/hikvision/binary_sensor.py | 1 - .../components/hikvisioncam/switch.py | 1 - homeassistant/components/hipchat/notify.py | 2 -- homeassistant/components/history/__init__.py | 2 -- .../components/history_graph/__init__.py | 2 -- .../components/history_stats/sensor.py | 2 -- homeassistant/components/hive/__init__.py | 2 -- .../components/hive/binary_sensor.py | 2 -- homeassistant/components/hive/climate.py | 2 -- homeassistant/components/hive/light.py | 2 -- homeassistant/components/hive/sensor.py | 2 -- homeassistant/components/hive/switch.py | 2 -- homeassistant/components/hlk_sw16/__init__.py | 2 -- homeassistant/components/hlk_sw16/switch.py | 4 +--- homeassistant/components/homekit/__init__.py | 2 -- .../components/homekit_controller/__init__.py | 2 -- .../homekit_controller/alarm_control_panel.py | 2 -- .../homekit_controller/binary_sensor.py | 2 -- .../components/homekit_controller/climate.py | 2 -- .../components/homekit_controller/cover.py | 2 -- .../components/homekit_controller/light.py | 2 -- .../components/homekit_controller/lock.py | 2 -- .../components/homekit_controller/sensor.py | 2 -- .../components/homekit_controller/switch.py | 2 -- .../components/homematic/__init__.py | 2 -- .../components/homematic/binary_sensor.py | 2 -- homeassistant/components/homematic/climate.py | 2 -- homeassistant/components/homematic/cover.py | 2 -- homeassistant/components/homematic/light.py | 2 -- homeassistant/components/homematic/lock.py | 2 -- homeassistant/components/homematic/notify.py | 2 -- homeassistant/components/homematic/sensor.py | 2 -- homeassistant/components/homematic/switch.py | 2 -- .../components/homematicip_cloud/__init__.py | 2 -- .../homematicip_cloud/alarm_control_panel.py | 2 -- .../homematicip_cloud/binary_sensor.py | 2 -- .../components/homematicip_cloud/cover.py | 2 -- .../components/homematicip_cloud/light.py | 2 -- .../components/homematicip_cloud/sensor.py | 2 -- .../components/homematicip_cloud/switch.py | 2 -- .../components/homematicip_cloud/weather.py | 2 -- .../components/homeworks/__init__.py | 2 -- homeassistant/components/homeworks/light.py | 2 -- homeassistant/components/honeywell/climate.py | 2 -- .../components/horizon/media_player.py | 2 -- homeassistant/components/hp_ilo/sensor.py | 2 -- homeassistant/components/html5/notify.py | 4 ---- homeassistant/components/http/__init__.py | 2 -- homeassistant/components/htu21d/sensor.py | 3 --- .../components/huawei_lte/__init__.py | 2 -- .../components/huawei_lte/device_tracker.py | 2 -- homeassistant/components/huawei_lte/notify.py | 2 -- homeassistant/components/huawei_lte/sensor.py | 2 -- homeassistant/components/hue/__init__.py | 2 -- homeassistant/components/hue/light.py | 1 - .../hunterdouglas_powerview/scene.py | 2 -- .../components/hydrawise/__init__.py | 2 -- .../components/hydrawise/binary_sensor.py | 2 -- homeassistant/components/hydrawise/sensor.py | 2 -- homeassistant/components/hydrawise/switch.py | 2 -- .../components/hydroquebec/sensor.py | 2 -- .../components/ialarm/alarm_control_panel.py | 2 -- .../components/icloud/device_tracker.py | 2 -- .../components/idteck_prox/__init__.py | 2 -- homeassistant/components/ifttt/__init__.py | 3 --- .../components/ifttt/alarm_control_panel.py | 2 -- homeassistant/components/iglo/light.py | 2 -- homeassistant/components/ihc/__init__.py | 2 -- homeassistant/components/ihc/binary_sensor.py | 2 -- homeassistant/components/ihc/light.py | 2 -- homeassistant/components/ihc/sensor.py | 2 -- homeassistant/components/ihc/switch.py | 2 -- .../components/image_processing/__init__.py | 2 -- homeassistant/components/imap/sensor.py | 2 -- homeassistant/components/influxdb/__init__.py | 2 -- homeassistant/components/influxdb/sensor.py | 2 -- homeassistant/components/insteon/__init__.py | 2 -- .../components/insteon/binary_sensor.py | 2 -- homeassistant/components/insteon/cover.py | 1 - homeassistant/components/insteon/fan.py | 2 -- homeassistant/components/insteon/light.py | 2 -- homeassistant/components/insteon/sensor.py | 2 -- homeassistant/components/insteon/switch.py | 2 -- homeassistant/components/ios/__init__.py | 2 -- homeassistant/components/ios/notify.py | 2 -- homeassistant/components/ios/sensor.py | 2 -- homeassistant/components/iota/__init__.py | 2 -- homeassistant/components/iota/sensor.py | 2 -- homeassistant/components/iperf3/__init__.py | 2 -- homeassistant/components/iperf3/sensor.py | 2 -- homeassistant/components/ipma/weather.py | 2 -- .../components/irish_rail_transport/sensor.py | 2 -- .../components/islamic_prayer_times/sensor.py | 2 -- homeassistant/components/iss/binary_sensor.py | 2 -- homeassistant/components/isy994/__init__.py | 2 -- homeassistant/components/itach/remote.py | 2 -- .../components/jewish_calendar/sensor.py | 2 -- .../components/joaoapps_join/__init__.py | 2 -- .../components/joaoapps_join/notify.py | 2 -- homeassistant/components/juicenet/__init__.py | 2 -- homeassistant/components/juicenet/sensor.py | 2 -- .../keenetic_ndms2/device_tracker.py | 2 -- homeassistant/components/keyboard/__init__.py | 2 -- .../components/keyboard_remote/__init__.py | 2 -- homeassistant/components/kira/__init__.py | 2 -- homeassistant/components/kiwi/lock.py | 2 -- homeassistant/components/knx/__init__.py | 2 -- homeassistant/components/knx/binary_sensor.py | 2 -- homeassistant/components/knx/climate.py | 2 -- homeassistant/components/knx/cover.py | 2 -- homeassistant/components/knx/light.py | 1 - homeassistant/components/knx/notify.py | 2 -- homeassistant/components/knx/scene.py | 2 -- homeassistant/components/knx/sensor.py | 2 -- homeassistant/components/knx/switch.py | 2 -- homeassistant/components/kodi/media_player.py | 2 -- homeassistant/components/kodi/notify.py | 2 -- .../components/konnected/__init__.py | 4 ---- .../components/konnected/binary_sensor.py | 2 -- homeassistant/components/konnected/sensor.py | 2 -- homeassistant/components/konnected/switch.py | 2 -- homeassistant/components/kwb/sensor.py | 2 -- homeassistant/components/lacrosse/sensor.py | 2 -- homeassistant/components/lametric/__init__.py | 2 -- homeassistant/components/lametric/notify.py | 4 ---- homeassistant/components/lastfm/sensor.py | 2 -- .../components/launch_library/sensor.py | 2 -- homeassistant/components/lcn/__init__.py | 2 -- homeassistant/components/lcn/binary_sensor.py | 2 -- homeassistant/components/lcn/cover.py | 2 -- homeassistant/components/lcn/light.py | 2 -- homeassistant/components/lcn/sensor.py | 2 -- homeassistant/components/lcn/switch.py | 2 -- .../components/lg_netcast/media_player.py | 2 -- .../components/lg_soundbar/media_player.py | 2 -- homeassistant/components/lifx/__init__.py | 2 -- homeassistant/components/lifx/light.py | 3 --- homeassistant/components/lifx_legacy/light.py | 2 -- homeassistant/components/light/__init__.py | 1 - .../components/lightwave/__init__.py | 2 -- homeassistant/components/lightwave/light.py | 2 -- homeassistant/components/lightwave/switch.py | 2 -- .../components/limitlessled/light.py | 2 -- .../components/linksys_ap/device_tracker.py | 2 -- homeassistant/components/linky/sensor.py | 1 - homeassistant/components/linode/__init__.py | 2 -- .../components/linode/binary_sensor.py | 2 -- homeassistant/components/linode/switch.py | 2 -- .../components/linux_battery/sensor.py | 2 -- homeassistant/components/lirc/__init__.py | 2 -- homeassistant/components/litejet/__init__.py | 2 -- homeassistant/components/litejet/light.py | 2 -- homeassistant/components/litejet/scene.py | 2 -- homeassistant/components/litejet/switch.py | 2 -- .../components/liveboxplaytv/media_player.py | 2 -- homeassistant/components/locative/__init__.py | 2 -- .../components/locative/device_tracker.py | 2 -- homeassistant/components/lock/__init__.py | 1 - homeassistant/components/logbook/__init__.py | 2 -- .../components/logi_circle/__init__.py | 2 -- .../components/logi_circle/camera.py | 2 -- .../components/logi_circle/sensor.py | 2 -- .../components/london_underground/sensor.py | 2 -- homeassistant/components/loopenergy/sensor.py | 2 -- .../components/luci/device_tracker.py | 2 -- .../components/luftdaten/__init__.py | 2 -- homeassistant/components/luftdaten/sensor.py | 2 -- homeassistant/components/lupusec/__init__.py | 2 -- .../components/lupusec/alarm_control_panel.py | 2 -- .../components/lupusec/binary_sensor.py | 2 -- homeassistant/components/lupusec/switch.py | 2 -- homeassistant/components/lutron/__init__.py | 2 -- homeassistant/components/lutron/cover.py | 2 -- homeassistant/components/lutron/light.py | 2 -- homeassistant/components/lutron/scene.py | 2 -- homeassistant/components/lutron/switch.py | 2 -- .../components/lutron_caseta/__init__.py | 2 -- .../components/lutron_caseta/cover.py | 2 -- .../components/lutron_caseta/light.py | 2 -- .../components/lutron_caseta/scene.py | 2 -- .../components/lutron_caseta/switch.py | 2 -- homeassistant/components/lw12wifi/light.py | 2 -- homeassistant/components/lyft/sensor.py | 2 -- .../components/magicseaweed/sensor.py | 2 -- homeassistant/components/mailbox/__init__.py | 1 - homeassistant/components/mailgun/__init__.py | 1 - homeassistant/components/mailgun/notify.py | 4 ---- .../manual_mqtt/alarm_control_panel.py | 2 -- homeassistant/components/mastodon/notify.py | 2 -- homeassistant/components/matrix/__init__.py | 2 -- homeassistant/components/matrix/notify.py | 2 -- homeassistant/components/maxcube/__init__.py | 2 -- .../components/media_extractor/__init__.py | 3 --- .../components/media_player/__init__.py | 2 -- .../components/mediaroom/media_player.py | 2 -- homeassistant/components/melissa/__init__.py | 2 -- homeassistant/components/melissa/climate.py | 2 -- .../components/meraki/device_tracker.py | 1 - .../components/message_bird/notify.py | 2 -- homeassistant/components/met/weather.py | 2 -- .../components/meteo_france/__init__.py | 2 -- homeassistant/components/metoffice/sensor.py | 2 -- homeassistant/components/metoffice/weather.py | 2 -- homeassistant/components/mfi/sensor.py | 2 -- homeassistant/components/mfi/switch.py | 2 -- homeassistant/components/mhz19/sensor.py | 2 -- homeassistant/components/microsoft/tts.py | 2 -- .../components/microsoft_face/__init__.py | 1 - .../microsoft_face_detect/image_processing.py | 2 -- .../image_processing.py | 2 -- homeassistant/components/miflora/sensor.py | 2 -- .../components/mikrotik/device_tracker.py | 2 -- homeassistant/components/mill/climate.py | 2 -- homeassistant/components/mitemp_bt/sensor.py | 2 -- .../components/mobile_app/__init__.py | 4 ---- .../components/mobile_app/binary_sensor.py | 2 -- homeassistant/components/mobile_app/notify.py | 2 -- homeassistant/components/mobile_app/sensor.py | 2 -- homeassistant/components/mochad/__init__.py | 2 -- homeassistant/components/mochad/light.py | 2 -- homeassistant/components/mochad/switch.py | 2 -- homeassistant/components/modbus/__init__.py | 2 -- .../components/modbus/binary_sensor.py | 2 -- homeassistant/components/modbus/climate.py | 2 -- homeassistant/components/modbus/sensor.py | 2 -- homeassistant/components/modbus/switch.py | 2 -- .../components/modem_callerid/sensor.py | 2 -- .../components/monoprice/media_player.py | 2 -- homeassistant/components/mopar/__init__.py | 2 -- homeassistant/components/mopar/lock.py | 2 -- homeassistant/components/mopar/sensor.py | 1 - homeassistant/components/mopar/switch.py | 2 -- homeassistant/components/mpd/media_player.py | 2 -- homeassistant/components/mqtt/__init__.py | 2 -- .../components/mqtt/alarm_control_panel.py | 2 -- .../components/mqtt/binary_sensor.py | 2 -- homeassistant/components/mqtt/camera.py | 2 -- homeassistant/components/mqtt/climate.py | 2 -- homeassistant/components/mqtt/cover.py | 2 -- .../components/mqtt/device_tracker.py | 2 -- homeassistant/components/mqtt/fan.py | 2 -- .../components/mqtt/light/__init__.py | 2 -- .../components/mqtt/light/schema_basic.py | 2 -- .../components/mqtt/light/schema_json.py | 2 -- .../components/mqtt/light/schema_template.py | 2 -- homeassistant/components/mqtt/lock.py | 2 -- homeassistant/components/mqtt/sensor.py | 2 -- homeassistant/components/mqtt/server.py | 4 ---- homeassistant/components/mqtt/switch.py | 2 -- homeassistant/components/mqtt/vacuum.py | 2 -- .../components/mqtt_eventstream/__init__.py | 2 -- .../components/mqtt_json/device_tracker.py | 2 -- homeassistant/components/mqtt_room/sensor.py | 2 -- .../components/mqtt_statestream/__init__.py | 1 - homeassistant/components/mvglive/sensor.py | 2 -- homeassistant/components/mychevy/__init__.py | 2 -- homeassistant/components/mycroft/__init__.py | 2 -- homeassistant/components/mycroft/notify.py | 2 -- homeassistant/components/myq/cover.py | 1 - .../components/mysensors/__init__.py | 2 -- .../components/mystrom/binary_sensor.py | 2 -- homeassistant/components/mystrom/light.py | 2 -- homeassistant/components/mystrom/switch.py | 2 -- .../components/mythicbeastsdns/__init__.py | 2 -- homeassistant/components/n26/__init__.py | 2 -- homeassistant/components/n26/sensor.py | 2 -- homeassistant/components/n26/switch.py | 2 -- homeassistant/components/nad/media_player.py | 2 -- .../components/namecheapdns/__init__.py | 2 -- homeassistant/components/nanoleaf/light.py | 2 -- homeassistant/components/neato/__init__.py | 2 -- homeassistant/components/neato/camera.py | 2 -- homeassistant/components/neato/switch.py | 2 -- homeassistant/components/neato/vacuum.py | 2 -- .../nederlandse_spoorwegen/sensor.py | 2 -- homeassistant/components/nello/lock.py | 2 -- .../components/ness_alarm/__init__.py | 2 -- .../ness_alarm/alarm_control_panel.py | 2 -- .../components/ness_alarm/binary_sensor.py | 1 - homeassistant/components/nest/__init__.py | 2 -- .../components/nest/binary_sensor.py | 2 -- homeassistant/components/nest/camera.py | 2 -- homeassistant/components/nest/climate.py | 1 - homeassistant/components/nest/sensor.py | 2 -- homeassistant/components/netatmo/__init__.py | 3 --- .../components/netatmo/binary_sensor.py | 2 -- homeassistant/components/netatmo/camera.py | 2 -- homeassistant/components/netatmo/climate.py | 2 -- homeassistant/components/netatmo/sensor.py | 2 -- .../components/netatmo_public/sensor.py | 2 -- homeassistant/components/netdata/sensor.py | 2 -- .../components/netgear/device_tracker.py | 2 -- .../components/netgear_lte/__init__.py | 2 -- .../components/netgear_lte/binary_sensor.py | 2 -- .../components/netgear_lte/notify.py | 2 -- .../components/netgear_lte/sensor.py | 2 -- homeassistant/components/netio/switch.py | 3 --- .../components/neurio_energy/sensor.py | 2 -- .../components/niko_home_control/light.py | 2 -- homeassistant/components/nilu/air_quality.py | 2 -- .../components/nissan_leaf/__init__.py | 2 -- .../components/nissan_leaf/binary_sensor.py | 2 -- .../components/nissan_leaf/device_tracker.py | 2 -- .../components/nissan_leaf/sensor.py | 2 -- .../components/nissan_leaf/switch.py | 2 -- .../components/nmap_tracker/device_tracker.py | 2 -- homeassistant/components/nmbs/sensor.py | 2 -- homeassistant/components/noaa_tides/sensor.py | 2 -- .../components/norway_air/air_quality.py | 2 -- .../components/nsw_fuel_station/sensor.py | 2 -- .../geo_location.py | 2 -- homeassistant/components/nuheat/__init__.py | 2 -- homeassistant/components/nuheat/climate.py | 2 -- .../components/nuimo_controller/__init__.py | 4 ---- homeassistant/components/nuki/lock.py | 2 -- homeassistant/components/nut/sensor.py | 2 -- .../components/nx584/alarm_control_panel.py | 2 -- .../components/nx584/binary_sensor.py | 2 -- .../components/oasa_telematics/sensor.py | 1 - .../components/octoprint/binary_sensor.py | 2 -- homeassistant/components/octoprint/sensor.py | 2 -- homeassistant/components/oem/climate.py | 2 -- homeassistant/components/ohmconnect/sensor.py | 2 -- .../components/onboarding/__init__.py | 2 -- .../components/onkyo/media_player.py | 2 -- homeassistant/components/onvif/camera.py | 4 ---- .../components/opencv/image_processing.py | 2 -- homeassistant/components/openevse/sensor.py | 2 -- .../components/openhome/media_player.py | 2 -- .../components/opensensemap/air_quality.py | 2 -- .../components/opentherm_gw/__init__.py | 2 -- .../components/opentherm_gw/binary_sensor.py | 2 -- .../components/opentherm_gw/climate.py | 2 -- .../components/opentherm_gw/sensor.py | 2 -- homeassistant/components/openuv/__init__.py | 2 -- .../components/openuv/binary_sensor.py | 2 -- homeassistant/components/openuv/sensor.py | 2 -- .../components/openweathermap/sensor.py | 2 -- .../components/openweathermap/weather.py | 2 -- homeassistant/components/opple/light.py | 2 -- homeassistant/components/orvibo/switch.py | 2 -- .../components/osramlightify/light.py | 2 -- homeassistant/components/otp/sensor.py | 2 -- homeassistant/components/owlet/__init__.py | 2 -- .../components/owntracks/__init__.py | 4 ---- .../components/owntracks/device_tracker.py | 2 -- .../panasonic_bluray/media_player.py | 2 -- .../panasonic_viera/media_player.py | 2 -- .../components/pandora/media_player.py | 1 - .../components/panel_custom/__init__.py | 2 -- .../components/panel_iframe/__init__.py | 2 -- homeassistant/components/pencom/switch.py | 2 -- .../components/philips_js/media_player.py | 2 -- homeassistant/components/pi_hole/sensor.py | 2 -- homeassistant/components/piglow/light.py | 2 -- homeassistant/components/pilight/__init__.py | 2 -- .../components/pilight/binary_sensor.py | 2 -- homeassistant/components/pilight/sensor.py | 2 -- homeassistant/components/pilight/switch.py | 2 -- .../components/pjlink/media_player.py | 2 -- homeassistant/components/plant/__init__.py | 2 -- homeassistant/components/plex/media_player.py | 2 -- homeassistant/components/plex/sensor.py | 2 -- .../components/plum_lightpad/__init__.py | 2 -- .../components/plum_lightpad/light.py | 2 -- .../components/pocketcasts/sensor.py | 2 -- homeassistant/components/point/__init__.py | 4 ---- homeassistant/components/pollen/sensor.py | 2 -- homeassistant/components/postnl/sensor.py | 2 -- .../components/prezzibenzina/sensor.py | 2 -- homeassistant/components/proliphix/climate.py | 2 -- .../components/prometheus/__init__.py | 4 ---- .../components/proximity/__init__.py | 1 - homeassistant/components/proxy/camera.py | 2 -- homeassistant/components/ps4/__init__.py | 2 -- homeassistant/components/ps4/media_player.py | 2 -- homeassistant/components/push/camera.py | 2 -- homeassistant/components/pushbullet/notify.py | 2 -- homeassistant/components/pushbullet/sensor.py | 2 -- homeassistant/components/pushetta/notify.py | 2 -- homeassistant/components/pushover/notify.py | 1 - .../components/python_script/__init__.py | 2 -- .../components/qbittorrent/sensor.py | 2 -- homeassistant/components/qnap/sensor.py | 2 -- .../components/qrcode/image_processing.py | 2 -- .../quantum_gateway/device_tracker.py | 2 -- .../components/qwikswitch/__init__.py | 2 -- .../components/qwikswitch/binary_sensor.py | 2 -- homeassistant/components/qwikswitch/light.py | 2 -- homeassistant/components/qwikswitch/sensor.py | 2 -- homeassistant/components/qwikswitch/switch.py | 2 -- homeassistant/components/rachio/__init__.py | 2 -- .../components/rachio/binary_sensor.py | 2 -- homeassistant/components/rachio/switch.py | 2 -- .../components/radiotherm/climate.py | 2 -- homeassistant/components/rainbird/__init__.py | 2 -- homeassistant/components/rainbird/sensor.py | 2 -- homeassistant/components/rainbird/switch.py | 2 -- .../components/raincloud/__init__.py | 2 -- .../components/raincloud/binary_sensor.py | 2 -- homeassistant/components/raincloud/sensor.py | 2 -- homeassistant/components/raincloud/switch.py | 2 -- .../components/rainmachine/__init__.py | 2 -- .../components/rainmachine/binary_sensor.py | 1 - .../components/rainmachine/sensor.py | 1 - .../components/rainmachine/switch.py | 2 -- .../components/raspihats/__init__.py | 2 -- .../components/raspihats/binary_sensor.py | 2 -- homeassistant/components/raspihats/switch.py | 2 -- homeassistant/components/raspyrfm/switch.py | 1 - .../components/recollect_waste/sensor.py | 2 -- homeassistant/components/recorder/__init__.py | 2 -- homeassistant/components/recswitch/switch.py | 2 -- homeassistant/components/reddit/sensor.py | 2 -- .../components/rejseplanen/sensor.py | 1 - .../components/remember_the_milk/__init__.py | 2 -- homeassistant/components/remote/__init__.py | 1 - homeassistant/components/rflink/__init__.py | 2 -- .../components/rflink/binary_sensor.py | 2 -- homeassistant/components/rflink/cover.py | 2 -- homeassistant/components/rflink/light.py | 2 -- homeassistant/components/rflink/sensor.py | 2 -- homeassistant/components/rflink/switch.py | 2 -- homeassistant/components/rfxtrx/__init__.py | 2 -- .../components/rfxtrx/binary_sensor.py | 2 -- homeassistant/components/rfxtrx/cover.py | 2 -- homeassistant/components/rfxtrx/light.py | 2 -- homeassistant/components/rfxtrx/sensor.py | 2 -- homeassistant/components/rfxtrx/switch.py | 2 -- homeassistant/components/ring/__init__.py | 2 -- .../components/ring/binary_sensor.py | 2 -- homeassistant/components/ring/camera.py | 2 -- homeassistant/components/ring/sensor.py | 2 -- homeassistant/components/ripple/sensor.py | 2 -- .../components/ritassist/device_tracker.py | 2 -- .../components/rmvtransport/sensor.py | 2 -- homeassistant/components/rocketchat/notify.py | 2 -- homeassistant/components/roku/__init__.py | 2 -- homeassistant/components/roku/media_player.py | 2 -- homeassistant/components/roku/remote.py | 2 -- homeassistant/components/roomba/vacuum.py | 2 -- homeassistant/components/route53/__init__.py | 2 -- homeassistant/components/rova/sensor.py | 2 -- homeassistant/components/rpi_gpio/__init__.py | 2 -- .../components/rpi_gpio/binary_sensor.py | 2 -- homeassistant/components/rpi_gpio/cover.py | 2 -- homeassistant/components/rpi_gpio/switch.py | 2 -- .../components/rpi_gpio_pwm/light.py | 2 -- homeassistant/components/rpi_pfio/__init__.py | 2 -- .../components/rpi_pfio/binary_sensor.py | 2 -- homeassistant/components/rpi_pfio/switch.py | 2 -- homeassistant/components/rpi_rf/switch.py | 2 -- .../components/rss_feed_template/__init__.py | 2 -- .../components/russound_rio/media_player.py | 2 -- .../components/russound_rnet/media_player.py | 2 -- homeassistant/components/ruter/sensor.py | 2 -- homeassistant/components/sabnzbd/__init__.py | 2 -- homeassistant/components/sabnzbd/sensor.py | 2 -- .../components/samsungtv/media_player.py | 2 -- .../components/satel_integra/__init__.py | 2 -- .../satel_integra/alarm_control_panel.py | 2 -- .../components/satel_integra/binary_sensor.py | 2 -- homeassistant/components/scrape/sensor.py | 2 -- homeassistant/components/script/__init__.py | 2 -- homeassistant/components/scsgate/__init__.py | 2 -- homeassistant/components/scsgate/cover.py | 2 -- homeassistant/components/scsgate/light.py | 2 -- homeassistant/components/scsgate/switch.py | 2 -- homeassistant/components/season/sensor.py | 2 -- homeassistant/components/sendgrid/notify.py | 2 -- homeassistant/components/sense/__init__.py | 2 -- .../components/sense/binary_sensor.py | 2 -- homeassistant/components/sense/sensor.py | 2 -- homeassistant/components/sensehat/light.py | 2 -- homeassistant/components/sensehat/sensor.py | 2 -- homeassistant/components/sensibo/climate.py | 2 -- homeassistant/components/serial/sensor.py | 2 -- homeassistant/components/serial_pm/sensor.py | 2 -- homeassistant/components/sesame/lock.py | 2 -- .../components/seventeentrack/sensor.py | 1 - homeassistant/components/shiftr/__init__.py | 2 -- homeassistant/components/shodan/sensor.py | 2 -- .../components/shopping_list/__init__.py | 1 - homeassistant/components/sht31/sensor.py | 3 --- homeassistant/components/simplepush/notify.py | 2 -- .../components/simplisafe/__init__.py | 2 -- homeassistant/components/sisyphus/__init__.py | 2 -- homeassistant/components/sisyphus/light.py | 2 -- .../components/sisyphus/media_player.py | 2 -- homeassistant/components/skybeacon/sensor.py | 2 -- homeassistant/components/skybell/__init__.py | 2 -- .../components/skybell/binary_sensor.py | 2 -- homeassistant/components/skybell/camera.py | 2 -- homeassistant/components/skybell/light.py | 2 -- homeassistant/components/skybell/sensor.py | 2 -- homeassistant/components/skybell/switch.py | 2 -- homeassistant/components/slack/notify.py | 2 -- homeassistant/components/sleepiq/__init__.py | 2 -- .../components/sleepiq/binary_sensor.py | 2 -- homeassistant/components/sleepiq/sensor.py | 1 - homeassistant/components/sma/sensor.py | 2 -- homeassistant/components/smappee/__init__.py | 2 -- homeassistant/components/smappee/sensor.py | 2 -- homeassistant/components/smappee/switch.py | 2 -- .../components/smartthings/__init__.py | 3 --- .../components/smartthings/binary_sensor.py | 2 -- .../components/smartthings/climate.py | 2 -- homeassistant/components/smartthings/cover.py | 2 -- homeassistant/components/smartthings/fan.py | 2 -- homeassistant/components/smartthings/light.py | 2 -- homeassistant/components/smartthings/lock.py | 2 -- homeassistant/components/smartthings/scene.py | 2 -- .../components/smartthings/sensor.py | 2 -- .../components/smartthings/switch.py | 2 -- homeassistant/components/smhi/__init__.py | 2 -- homeassistant/components/smhi/weather.py | 2 -- .../components/snapcast/media_player.py | 2 -- homeassistant/components/snips/__init__.py | 2 -- .../components/snmp/device_tracker.py | 2 -- homeassistant/components/snmp/sensor.py | 2 -- homeassistant/components/snmp/switch.py | 2 -- homeassistant/components/sochain/sensor.py | 2 -- .../components/socialblade/sensor.py | 2 -- homeassistant/components/solaredge/sensor.py | 2 -- .../components/somfy_mylink/__init__.py | 1 - .../components/somfy_mylink/cover.py | 1 - .../components/songpal/media_player.py | 2 -- homeassistant/components/sonos/__init__.py | 1 - .../components/sony_projector/switch.py | 2 -- .../components/soundtouch/media_player.py | 2 -- homeassistant/components/spaceapi/__init__.py | 1 - homeassistant/components/spc/__init__.py | 2 -- .../components/speedtestdotnet/__init__.py | 2 -- .../components/speedtestdotnet/sensor.py | 2 -- homeassistant/components/spider/__init__.py | 2 -- homeassistant/components/spider/climate.py | 2 -- homeassistant/components/spider/switch.py | 2 -- homeassistant/components/spotcrime/sensor.py | 2 -- .../components/spotify/media_player.py | 3 --- homeassistant/components/sql/sensor.py | 2 -- homeassistant/components/srp_energy/sensor.py | 2 -- .../components/starlingbank/sensor.py | 2 -- homeassistant/components/startca/sensor.py | 2 -- homeassistant/components/statsd/__init__.py | 2 -- .../components/steam_online/sensor.py | 2 -- homeassistant/components/stream/__init__.py | 4 ---- homeassistant/components/stride/notify.py | 2 -- .../swiss_hydrological_data/sensor.py | 2 -- .../swiss_public_transport/sensor.py | 2 -- homeassistant/components/switch/__init__.py | 1 - homeassistant/components/switchbot/switch.py | 2 -- homeassistant/components/switchmate/switch.py | 2 -- homeassistant/components/syncthru/sensor.py | 2 -- homeassistant/components/synology/camera.py | 2 -- .../components/synology_srm/device_tracker.py | 2 -- .../components/synologydsm/sensor.py | 2 -- .../components/system_health/__init__.py | 1 - .../components/system_log/__init__.py | 1 - .../components/systemmonitor/sensor.py | 2 -- homeassistant/components/sytadin/sensor.py | 2 -- homeassistant/components/tado/__init__.py | 2 -- homeassistant/components/tahoma/__init__.py | 2 -- .../components/tahoma/binary_sensor.py | 2 -- homeassistant/components/tahoma/cover.py | 2 -- homeassistant/components/tahoma/scene.py | 2 -- homeassistant/components/tahoma/sensor.py | 2 -- homeassistant/components/tahoma/switch.py | 2 -- .../components/tank_utility/sensor.py | 4 ---- .../components/tapsaff/binary_sensor.py | 2 -- homeassistant/components/tautulli/sensor.py | 2 -- homeassistant/components/ted5000/sensor.py | 2 -- homeassistant/components/telegram/notify.py | 2 -- .../components/telegram_bot/__init__.py | 2 -- .../components/telegram_bot/webhooks.py | 2 -- .../components/tellduslive/__init__.py | 2 -- .../components/tellstick/__init__.py | 2 -- homeassistant/components/tellstick/sensor.py | 2 -- homeassistant/components/temper/sensor.py | 2 -- .../components/tensorflow/image_processing.py | 2 -- homeassistant/components/tesla/__init__.py | 2 -- .../components/tesla/binary_sensor.py | 2 -- homeassistant/components/tesla/climate.py | 2 -- .../components/tesla/device_tracker.py | 2 -- homeassistant/components/tesla/lock.py | 2 -- homeassistant/components/tesla/sensor.py | 2 -- homeassistant/components/tesla/switch.py | 1 - homeassistant/components/tfiac/climate.py | 2 -- .../components/thermoworks_smoke/sensor.py | 2 -- .../components/thethingsnetwork/sensor.py | 2 -- .../components/thingspeak/__init__.py | 2 -- .../components/thinkingcleaner/sensor.py | 2 -- .../components/thinkingcleaner/switch.py | 2 -- homeassistant/components/tibber/__init__.py | 2 -- homeassistant/components/tikteck/light.py | 2 -- .../components/tile/device_tracker.py | 2 -- homeassistant/components/todoist/calendar.py | 2 -- homeassistant/components/tof/sensor.py | 4 ---- homeassistant/components/toon/__init__.py | 2 -- .../components/toon/binary_sensor.py | 2 -- homeassistant/components/toon/climate.py | 2 -- homeassistant/components/toon/sensor.py | 2 -- homeassistant/components/torque/sensor.py | 1 - .../totalconnect/alarm_control_panel.py | 2 -- homeassistant/components/touchline/climate.py | 2 -- homeassistant/components/tplink/__init__.py | 2 -- .../components/tplink/device_tracker.py | 2 -- homeassistant/components/tplink/light.py | 2 -- homeassistant/components/tplink/switch.py | 2 -- .../components/tplink_lte/__init__.py | 2 -- homeassistant/components/tplink_lte/notify.py | 2 -- .../components/traccar/device_tracker.py | 2 -- .../components/trackr/device_tracker.py | 2 -- homeassistant/components/tradfri/__init__.py | 2 -- homeassistant/components/tradfri/light.py | 1 - homeassistant/components/tradfri/sensor.py | 2 -- homeassistant/components/tradfri/switch.py | 1 - .../trafikverket_weatherstation/sensor.py | 2 -- .../components/transmission/__init__.py | 2 -- .../components/transmission/sensor.py | 2 -- .../components/transmission/switch.py | 2 -- .../components/transport_nsw/sensor.py | 2 -- homeassistant/components/travisci/sensor.py | 2 -- .../components/trend/binary_sensor.py | 2 -- homeassistant/components/tts/__init__.py | 3 --- homeassistant/components/tuya/__init__.py | 2 -- homeassistant/components/tuya/climate.py | 1 - homeassistant/components/tuya/cover.py | 2 -- homeassistant/components/tuya/fan.py | 2 -- homeassistant/components/tuya/light.py | 2 -- homeassistant/components/tuya/scene.py | 2 -- homeassistant/components/tuya/switch.py | 2 -- homeassistant/components/twilio/__init__.py | 3 --- .../components/twilio_call/notify.py | 2 -- homeassistant/components/twilio_sms/notify.py | 2 -- homeassistant/components/twitch/sensor.py | 2 -- homeassistant/components/twitter/notify.py | 2 -- .../components/ubee/device_tracker.py | 2 -- homeassistant/components/uber/sensor.py | 2 -- homeassistant/components/unifi/__init__.py | 2 -- .../components/unifi/device_tracker.py | 2 -- homeassistant/components/unifi/switch.py | 3 +-- .../components/unifi_direct/device_tracker.py | 2 -- .../components/upc_connect/device_tracker.py | 2 -- homeassistant/components/upcloud/__init__.py | 2 -- .../components/upcloud/binary_sensor.py | 2 -- homeassistant/components/upcloud/switch.py | 2 -- homeassistant/components/updater/__init__.py | 2 -- homeassistant/components/upnp/__init__.py | 2 -- homeassistant/components/upnp/sensor.py | 2 -- homeassistant/components/ups/sensor.py | 2 -- .../components/uptimerobot/binary_sensor.py | 2 -- homeassistant/components/uscis/sensor.py | 2 -- .../usgs_earthquakes_feed/geo_location.py | 2 -- homeassistant/components/usps/__init__.py | 2 -- homeassistant/components/usps/camera.py | 2 -- homeassistant/components/usps/sensor.py | 2 -- homeassistant/components/uvc/camera.py | 2 -- homeassistant/components/vacuum/__init__.py | 2 -- homeassistant/components/vasttrafik/sensor.py | 2 -- homeassistant/components/velbus/__init__.py | 2 -- .../components/velbus/binary_sensor.py | 2 -- homeassistant/components/velbus/climate.py | 2 -- homeassistant/components/velbus/cover.py | 2 -- homeassistant/components/velbus/sensor.py | 2 -- homeassistant/components/velbus/switch.py | 2 -- homeassistant/components/velux/__init__.py | 2 -- homeassistant/components/velux/cover.py | 2 -- homeassistant/components/velux/scene.py | 2 -- homeassistant/components/venstar/climate.py | 2 -- homeassistant/components/vera/__init__.py | 2 -- .../components/vera/binary_sensor.py | 2 -- homeassistant/components/vera/climate.py | 2 -- homeassistant/components/vera/cover.py | 2 -- homeassistant/components/vera/light.py | 2 -- homeassistant/components/vera/lock.py | 2 -- homeassistant/components/vera/scene.py | 2 -- homeassistant/components/vera/sensor.py | 2 -- homeassistant/components/vera/switch.py | 2 -- homeassistant/components/verisure/__init__.py | 2 -- homeassistant/components/version/sensor.py | 2 -- homeassistant/components/vesync/switch.py | 2 -- .../components/vizio/media_player.py | 2 -- homeassistant/components/vlc/media_player.py | 2 -- .../components/volkszaehler/sensor.py | 2 -- .../components/volvooncall/__init__.py | 2 -- homeassistant/components/vultr/__init__.py | 2 -- .../components/vultr/binary_sensor.py | 2 -- homeassistant/components/vultr/sensor.py | 2 -- homeassistant/components/vultr/switch.py | 2 -- homeassistant/components/w800rf32/__init__.py | 2 -- .../components/w800rf32/binary_sensor.py | 1 - .../components/wake_on_lan/__init__.py | 2 -- .../components/wake_on_lan/switch.py | 2 -- homeassistant/components/waqi/sensor.py | 2 -- .../components/waterfurnace/__init__.py | 2 -- .../components/watson_iot/__init__.py | 2 -- .../components/waze_travel_time/sensor.py | 2 -- homeassistant/components/webhook/__init__.py | 2 -- .../components/webostv/media_player.py | 2 -- homeassistant/components/webostv/notify.py | 2 -- homeassistant/components/wemo/__init__.py | 2 -- .../components/wemo/binary_sensor.py | 2 -- homeassistant/components/wemo/fan.py | 1 - homeassistant/components/wemo/light.py | 2 -- homeassistant/components/wemo/switch.py | 1 - homeassistant/components/whois/sensor.py | 2 -- homeassistant/components/wink/__init__.py | 2 -- .../components/wink/alarm_control_panel.py | 2 -- .../components/wink/binary_sensor.py | 2 -- homeassistant/components/wink/climate.py | 2 -- homeassistant/components/wink/cover.py | 2 -- homeassistant/components/wink/fan.py | 2 -- homeassistant/components/wink/light.py | 2 -- homeassistant/components/wink/lock.py | 2 -- homeassistant/components/wink/scene.py | 2 -- homeassistant/components/wink/sensor.py | 2 -- homeassistant/components/wink/switch.py | 2 -- .../components/wirelesstag/__init__.py | 2 -- .../components/wirelesstag/binary_sensor.py | 2 -- .../components/wirelesstag/sensor.py | 2 -- .../components/wirelesstag/switch.py | 2 -- .../components/workday/binary_sensor.py | 2 -- .../components/wunderlist/__init__.py | 2 -- homeassistant/components/xbox_live/sensor.py | 2 -- homeassistant/components/xeoma/camera.py | 2 -- .../components/xfinity/device_tracker.py | 2 -- homeassistant/components/xiaomi/camera.py | 1 - .../components/xiaomi_aqara/__init__.py | 2 -- .../components/xiaomi_miio/device_tracker.py | 2 -- homeassistant/components/xiaomi_miio/fan.py | 2 -- homeassistant/components/xiaomi_miio/light.py | 2 -- .../components/xiaomi_miio/remote.py | 2 -- .../components/xiaomi_miio/sensor.py | 2 -- .../components/xiaomi_miio/switch.py | 2 -- .../components/xiaomi_miio/vacuum.py | 2 -- .../components/xiaomi_tv/media_player.py | 2 -- homeassistant/components/xmpp/notify.py | 2 -- homeassistant/components/xs1/__init__.py | 2 -- homeassistant/components/xs1/climate.py | 1 - homeassistant/components/xs1/sensor.py | 1 - homeassistant/components/xs1/switch.py | 2 -- .../yale_smart_alarm/alarm_control_panel.py | 2 -- .../components/yamaha/media_player.py | 2 -- .../yamaha_musiccast/media_player.py | 2 -- homeassistant/components/yeelight/__init__.py | 2 -- .../components/yeelight/binary_sensor.py | 2 -- homeassistant/components/yeelight/light.py | 2 -- .../components/yeelightsunflower/light.py | 2 -- homeassistant/components/yessssms/notify.py | 2 -- homeassistant/components/yi/camera.py | 2 -- homeassistant/components/yr/sensor.py | 2 -- homeassistant/components/yweather/sensor.py | 2 -- homeassistant/components/yweather/weather.py | 2 -- homeassistant/components/zabbix/__init__.py | 2 -- homeassistant/components/zabbix/sensor.py | 2 -- homeassistant/components/zengge/light.py | 2 -- homeassistant/components/zeroconf/__init__.py | 3 --- homeassistant/components/zestimate/sensor.py | 2 -- homeassistant/components/zha/__init__.py | 8 -------- homeassistant/components/zha/binary_sensor.py | 2 -- homeassistant/components/zha/fan.py | 2 -- homeassistant/components/zha/light.py | 2 -- homeassistant/components/zha/sensor.py | 2 -- homeassistant/components/zha/switch.py | 2 -- .../components/zhong_hong/climate.py | 2 -- homeassistant/components/zigbee/__init__.py | 2 -- .../components/zigbee/binary_sensor.py | 2 -- homeassistant/components/zigbee/light.py | 2 -- homeassistant/components/zigbee/sensor.py | 2 -- homeassistant/components/zigbee/switch.py | 2 -- .../ziggo_mediabox_xl/media_player.py | 2 -- .../components/zoneminder/__init__.py | 2 -- .../components/zoneminder/binary_sensor.py | 2 -- homeassistant/components/zoneminder/camera.py | 2 -- homeassistant/components/zoneminder/sensor.py | 2 -- homeassistant/components/zoneminder/switch.py | 2 -- homeassistant/components/zwave/__init__.py | 2 -- 1156 files changed, 2 insertions(+), 2320 deletions(-) diff --git a/homeassistant/components/abode/__init__.py b/homeassistant/components/abode/__init__.py index 591bae1a9cf665..3a64a5e31f0104 100644 --- a/homeassistant/components/abode/__init__.py +++ b/homeassistant/components/abode/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['abodepy==0.15.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by goabode.com" diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index d7426e04166dc5..d1d75b7417ea46 100644 --- a/homeassistant/components/abode/alarm_control_panel.py +++ b/homeassistant/components/abode/alarm_control_panel.py @@ -8,8 +8,6 @@ from . import ATTRIBUTION, DOMAIN as ABODE_DOMAIN, AbodeDevice -DEPENDENCIES = ['abode'] - _LOGGER = logging.getLogger(__name__) ICON = 'mdi:security' diff --git a/homeassistant/components/abode/binary_sensor.py b/homeassistant/components/abode/binary_sensor.py index 874723420ed811..e3f74e9f4ec12e 100644 --- a/homeassistant/components/abode/binary_sensor.py +++ b/homeassistant/components/abode/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['abode'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a sensor for an Abode device.""" diff --git a/homeassistant/components/abode/camera.py b/homeassistant/components/abode/camera.py index d37644eccc397e..d0e4e833029fc0 100644 --- a/homeassistant/components/abode/camera.py +++ b/homeassistant/components/abode/camera.py @@ -9,8 +9,6 @@ from . import DOMAIN as ABODE_DOMAIN, AbodeDevice -DEPENDENCIES = ['abode'] - MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/cover.py b/homeassistant/components/abode/cover.py index c40159164dc4db..4c868daf4ba932 100644 --- a/homeassistant/components/abode/cover.py +++ b/homeassistant/components/abode/cover.py @@ -5,8 +5,6 @@ from . import DOMAIN as ABODE_DOMAIN, AbodeDevice -DEPENDENCIES = ['abode'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/light.py b/homeassistant/components/abode/light.py index 9e88acce41f053..6b3e5025c51405 100644 --- a/homeassistant/components/abode/light.py +++ b/homeassistant/components/abode/light.py @@ -10,8 +10,6 @@ from . import DOMAIN as ABODE_DOMAIN, AbodeDevice -DEPENDENCIES = ['abode'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/lock.py b/homeassistant/components/abode/lock.py index 0f568a4ace2492..c1272a3de5f405 100644 --- a/homeassistant/components/abode/lock.py +++ b/homeassistant/components/abode/lock.py @@ -5,8 +5,6 @@ from . import DOMAIN as ABODE_DOMAIN, AbodeDevice -DEPENDENCIES = ['abode'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/abode/sensor.py b/homeassistant/components/abode/sensor.py index ef6941c76d8b5f..b7e8fc1a118b0a 100644 --- a/homeassistant/components/abode/sensor.py +++ b/homeassistant/components/abode/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['abode'] - # Sensor types: Name, icon SENSOR_TYPES = { 'temp': ['Temperature', DEVICE_CLASS_TEMPERATURE], diff --git a/homeassistant/components/abode/switch.py b/homeassistant/components/abode/switch.py index 3e3ce031855fd0..74d1ea57bad445 100644 --- a/homeassistant/components/abode/switch.py +++ b/homeassistant/components/abode/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['abode'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Abode switch devices.""" diff --git a/homeassistant/components/acer_projector/switch.py b/homeassistant/components/acer_projector/switch.py index df6fb8816aae47..242f3f4a009d59 100644 --- a/homeassistant/components/acer_projector/switch.py +++ b/homeassistant/components/acer_projector/switch.py @@ -9,8 +9,6 @@ STATE_ON, STATE_OFF, STATE_UNKNOWN, CONF_NAME, CONF_FILENAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyserial==3.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_TIMEOUT = 'timeout' diff --git a/homeassistant/components/ads/__init__.py b/homeassistant/components/ads/__init__.py index 92c6ecb3335147..ba49f8abd9a3ec 100644 --- a/homeassistant/components/ads/__init__.py +++ b/homeassistant/components/ads/__init__.py @@ -14,8 +14,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyads==3.0.7'] - _LOGGER = logging.getLogger(__name__) DATA_ADS = 'data_ads' diff --git a/homeassistant/components/ads/binary_sensor.py b/homeassistant/components/ads/binary_sensor.py index baa44cb498fe04..b6f3a54e437192 100644 --- a/homeassistant/components/ads/binary_sensor.py +++ b/homeassistant/components/ads/binary_sensor.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'ADS binary sensor' -DEPENDENCIES = ['ads'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADS_VAR): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/ads/light.py b/homeassistant/components/ads/light.py index 49961565dced77..5168f49acdce08 100644 --- a/homeassistant/components/ads/light.py +++ b/homeassistant/components/ads/light.py @@ -12,7 +12,6 @@ AdsEntity, STATE_KEY_BRIGHTNESS, STATE_KEY_STATE _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ads'] DEFAULT_NAME = 'ADS Light' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADS_VAR): cv.string, diff --git a/homeassistant/components/ads/sensor.py b/homeassistant/components/ads/sensor.py index e74b8753d4b042..415eea09ddf4b6 100644 --- a/homeassistant/components/ads/sensor.py +++ b/homeassistant/components/ads/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "ADS sensor" -DEPENDENCIES = ['ads'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADS_VAR): cv.string, vol.Optional(CONF_ADS_FACTOR): cv.positive_int, diff --git a/homeassistant/components/ads/switch.py b/homeassistant/components/ads/switch.py index 0dfbeb811a068b..23e130f26958ef 100644 --- a/homeassistant/components/ads/switch.py +++ b/homeassistant/components/ads/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ads'] - DEFAULT_NAME = 'ADS Switch' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/aftership/sensor.py b/homeassistant/components/aftership/sensor.py index eefbb299a07125..fae3e38c96b3a3 100644 --- a/homeassistant/components/aftership/sensor.py +++ b/homeassistant/components/aftership/sensor.py @@ -13,8 +13,6 @@ from homeassistant.util import Throttle from .const import DOMAIN -REQUIREMENTS = ['pyaftership==0.1.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Information provided by AfterShip' diff --git a/homeassistant/components/airvisual/sensor.py b/homeassistant/components/airvisual/sensor.py index 7fad7bb35be0ec..55670020133054 100644 --- a/homeassistant/components/airvisual/sensor.py +++ b/homeassistant/components/airvisual/sensor.py @@ -13,7 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pyairvisual==3.0.1'] _LOGGER = getLogger(__name__) ATTR_CITY = 'city' diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 01146fecbb667c..70c85fd58a5c9b 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -9,8 +9,6 @@ STATE_OPENING, STATE_CLOSING, STATE_OPEN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['aladdin_connect==0.3'] - _LOGGER = logging.getLogger(__name__) NOTIFICATION_ID = 'aladdin_notification' diff --git a/homeassistant/components/alarmdecoder/__init__.py b/homeassistant/components/alarmdecoder/__init__.py index 5b1296b39de2c3..b4d1a2e0b9f7d7 100644 --- a/homeassistant/components/alarmdecoder/__init__.py +++ b/homeassistant/components/alarmdecoder/__init__.py @@ -10,8 +10,6 @@ from homeassistant.util import dt as dt_util from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA -REQUIREMENTS = ['alarmdecoder==1.13.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'alarmdecoder' diff --git a/homeassistant/components/alarmdecoder/alarm_control_panel.py b/homeassistant/components/alarmdecoder/alarm_control_panel.py index d7eced933ddbd1..51645b516b98d0 100644 --- a/homeassistant/components/alarmdecoder/alarm_control_panel.py +++ b/homeassistant/components/alarmdecoder/alarm_control_panel.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['alarmdecoder'] - SERVICE_ALARM_TOGGLE_CHIME = 'alarmdecoder_alarm_toggle_chime' ALARM_TOGGLE_CHIME_SCHEMA = vol.Schema({ vol.Required(ATTR_CODE): cv.string, diff --git a/homeassistant/components/alarmdecoder/binary_sensor.py b/homeassistant/components/alarmdecoder/binary_sensor.py index 09e63b4d664348..91ff8b381b57b3 100644 --- a/homeassistant/components/alarmdecoder/binary_sensor.py +++ b/homeassistant/components/alarmdecoder/binary_sensor.py @@ -8,8 +8,6 @@ CONF_ZONE_RFID, CONF_ZONE_TYPE, CONF_ZONES, SIGNAL_REL_MESSAGE, SIGNAL_RFX_MESSAGE, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE, ZONE_SCHEMA) -DEPENDENCIES = ['alarmdecoder'] - _LOGGER = logging.getLogger(__name__) ATTR_RF_BIT0 = 'rf_bit0' diff --git a/homeassistant/components/alarmdecoder/sensor.py b/homeassistant/components/alarmdecoder/sensor.py index 88371dad17a345..9fb37d62376bc6 100644 --- a/homeassistant/components/alarmdecoder/sensor.py +++ b/homeassistant/components/alarmdecoder/sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['alarmdecoder'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up for AlarmDecoder sensor devices.""" diff --git a/homeassistant/components/alarmdotcom/alarm_control_panel.py b/homeassistant/components/alarmdotcom/alarm_control_panel.py index ea581aca747fc8..5919bf84f41eb8 100644 --- a/homeassistant/components/alarmdotcom/alarm_control_panel.py +++ b/homeassistant/components/alarmdotcom/alarm_control_panel.py @@ -12,8 +12,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyalarmdotcom==0.3.2'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Alarm.com' diff --git a/homeassistant/components/alexa/__init__.py b/homeassistant/components/alexa/__init__.py index 062d698d5122a8..862605b64b5700 100644 --- a/homeassistant/components/alexa/__init__.py +++ b/homeassistant/components/alexa/__init__.py @@ -17,8 +17,6 @@ CONF_FLASH_BRIEFINGS = 'flash_briefings' CONF_SMART_HOME = 'smart_home' -DEPENDENCIES = ['http'] - ALEXA_ENTITY_SCHEMA = vol.Schema({ vol.Optional(smart_home.CONF_DESCRIPTION): cv.string, vol.Optional(smart_home.CONF_DISPLAY_CATEGORIES): cv.string, diff --git a/homeassistant/components/alpha_vantage/sensor.py b/homeassistant/components/alpha_vantage/sensor.py index 0eb57e5b27aeee..9ea6797a56efc3 100644 --- a/homeassistant/components/alpha_vantage/sensor.py +++ b/homeassistant/components/alpha_vantage/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['alpha_vantage==2.1.0'] - _LOGGER = logging.getLogger(__name__) ATTR_CLOSE = 'close' diff --git a/homeassistant/components/amazon_polly/tts.py b/homeassistant/components/amazon_polly/tts.py index d29ae32fb5723f..4511a587a60c53 100644 --- a/homeassistant/components/amazon_polly/tts.py +++ b/homeassistant/components/amazon_polly/tts.py @@ -6,8 +6,6 @@ from homeassistant.components.tts import PLATFORM_SCHEMA, Provider import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['boto3==1.9.16'] - _LOGGER = logging.getLogger(__name__) CONF_REGION = 'region_name' diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 944d4e14e7d26d..2c185c3bc71de5 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -20,8 +20,6 @@ ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE, TYPE_BINARY_SENSOR, TYPE_SENSOR) -REQUIREMENTS = ['aioambient==0.3.0'] - _LOGGER = logging.getLogger(__name__) DATA_CONFIG = 'config' diff --git a/homeassistant/components/ambient_station/binary_sensor.py b/homeassistant/components/ambient_station/binary_sensor.py index 04a38901683d94..02f7590c307eed 100644 --- a/homeassistant/components/ambient_station/binary_sensor.py +++ b/homeassistant/components/ambient_station/binary_sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ambient_station'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/ambient_station/sensor.py b/homeassistant/components/ambient_station/sensor.py index b394dc558e63c6..9c50d97fb0361c 100644 --- a/homeassistant/components/ambient_station/sensor.py +++ b/homeassistant/components/ambient_station/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ambient_station'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index a4c020efcdfa13..3a0a983fceb90f 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -12,9 +12,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['amcrest==1.3.0'] -DEPENDENCIES = ['ffmpeg'] - _LOGGER = logging.getLogger(__name__) CONF_AUTHENTICATION = 'authentication' diff --git a/homeassistant/components/amcrest/binary_sensor.py b/homeassistant/components/amcrest/binary_sensor.py index 113918ed041c3e..b591616a88d585 100644 --- a/homeassistant/components/amcrest/binary_sensor.py +++ b/homeassistant/components/amcrest/binary_sensor.py @@ -7,8 +7,6 @@ from homeassistant.const import CONF_NAME, CONF_BINARY_SENSORS from . import DATA_AMCREST, BINARY_SENSORS -DEPENDENCIES = ['amcrest'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index f361c4e0183e6b..07f5d403ba8742 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -12,8 +12,6 @@ from . import DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT -DEPENDENCIES = ['amcrest', 'ffmpeg'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/amcrest/sensor.py b/homeassistant/components/amcrest/sensor.py index 119520e6a03b5e..56cb021052e319 100644 --- a/homeassistant/components/amcrest/sensor.py +++ b/homeassistant/components/amcrest/sensor.py @@ -7,8 +7,6 @@ from . import DATA_AMCREST, SENSORS -DEPENDENCIES = ['amcrest'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/amcrest/switch.py b/homeassistant/components/amcrest/switch.py index 0bbd290b3ac253..90f750d17978d4 100644 --- a/homeassistant/components/amcrest/switch.py +++ b/homeassistant/components/amcrest/switch.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['amcrest'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/ampio/air_quality.py b/homeassistant/components/ampio/air_quality.py index f7aa98aec7c27c..339f490bae54f9 100644 --- a/homeassistant/components/ampio/air_quality.py +++ b/homeassistant/components/ampio/air_quality.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['asmog==0.0.6'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Data provided by Ampio' diff --git a/homeassistant/components/android_ip_webcam/__init__.py b/homeassistant/components/android_ip_webcam/__init__.py index 600efd55a16a1d..dfb6d143e9ae95 100644 --- a/homeassistant/components/android_ip_webcam/__init__.py +++ b/homeassistant/components/android_ip_webcam/__init__.py @@ -21,8 +21,6 @@ from homeassistant.components.mjpeg.camera import ( CONF_MJPEG_URL, CONF_STILL_IMAGE_URL) -REQUIREMENTS = ['pydroid-ipcam==0.8'] - _LOGGER = logging.getLogger(__name__) ATTR_AUD_CONNS = 'Audio Connections' diff --git a/homeassistant/components/android_ip_webcam/binary_sensor.py b/homeassistant/components/android_ip_webcam/binary_sensor.py index c058c44c5034e1..dbe50d8186245d 100644 --- a/homeassistant/components/android_ip_webcam/binary_sensor.py +++ b/homeassistant/components/android_ip_webcam/binary_sensor.py @@ -3,8 +3,6 @@ from . import CONF_HOST, CONF_NAME, DATA_IP_WEBCAM, KEY_MAP, AndroidIPCamEntity -DEPENDENCIES = ['android_ip_webcam'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/android_ip_webcam/sensor.py b/homeassistant/components/android_ip_webcam/sensor.py index 4d29493d64fba7..9748b6ba548b2b 100644 --- a/homeassistant/components/android_ip_webcam/sensor.py +++ b/homeassistant/components/android_ip_webcam/sensor.py @@ -5,8 +5,6 @@ CONF_HOST, CONF_NAME, CONF_SENSORS, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP, AndroidIPCamEntity) -DEPENDENCIES = ['android_ip_webcam'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/android_ip_webcam/switch.py b/homeassistant/components/android_ip_webcam/switch.py index 0304c5747f71ae..e894913f5a468b 100644 --- a/homeassistant/components/android_ip_webcam/switch.py +++ b/homeassistant/components/android_ip_webcam/switch.py @@ -5,8 +5,6 @@ CONF_HOST, CONF_NAME, CONF_SWITCHES, DATA_IP_WEBCAM, ICON_MAP, KEY_MAP, AndroidIPCamEntity) -DEPENDENCIES = ['android_ip_webcam'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 706ef6f8402430..2677144a144f40 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -18,8 +18,6 @@ ANDROIDTV_DOMAIN = 'androidtv' -REQUIREMENTS = ['androidtv==0.0.15'] - _LOGGER = logging.getLogger(__name__) SUPPORT_ANDROIDTV = SUPPORT_PAUSE | SUPPORT_PLAY | \ diff --git a/homeassistant/components/anel_pwrctrl/switch.py b/homeassistant/components/anel_pwrctrl/switch.py index b9b3070b97e737..7552e35fe4b237 100644 --- a/homeassistant/components/anel_pwrctrl/switch.py +++ b/homeassistant/components/anel_pwrctrl/switch.py @@ -10,8 +10,6 @@ from homeassistant.const import (CONF_HOST, CONF_PASSWORD, CONF_USERNAME) from homeassistant.util import Throttle -REQUIREMENTS = ['anel_pwrctrl-homeassistant==0.0.1.dev2'] - _LOGGER = logging.getLogger(__name__) CONF_PORT_RECV = 'port_recv' diff --git a/homeassistant/components/anthemav/media_player.py b/homeassistant/components/anthemav/media_player.py index c7ee579bc17cb0..1a335fc2ce6061 100644 --- a/homeassistant/components/anthemav/media_player.py +++ b/homeassistant/components/anthemav/media_player.py @@ -13,8 +13,6 @@ STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['anthemav==1.1.10'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'anthemav' diff --git a/homeassistant/components/apcupsd/__init__.py b/homeassistant/components/apcupsd/__init__.py index aab6f6dda018c6..d4649db0203c37 100644 --- a/homeassistant/components/apcupsd/__init__.py +++ b/homeassistant/components/apcupsd/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['apcaccess==0.0.13'] - _LOGGER = logging.getLogger(__name__) CONF_TYPE = 'type' diff --git a/homeassistant/components/apcupsd/binary_sensor.py b/homeassistant/components/apcupsd/binary_sensor.py index 445dab9b0744a0..367b3c2b9b5065 100644 --- a/homeassistant/components/apcupsd/binary_sensor.py +++ b/homeassistant/components/apcupsd/binary_sensor.py @@ -8,8 +8,6 @@ from homeassistant.components import apcupsd DEFAULT_NAME = 'UPS Online Status' -DEPENDENCIES = [apcupsd.DOMAIN] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) diff --git a/homeassistant/components/apcupsd/sensor.py b/homeassistant/components/apcupsd/sensor.py index 09f9b324bdd863..ae1ad10223d75e 100644 --- a/homeassistant/components/apcupsd/sensor.py +++ b/homeassistant/components/apcupsd/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = [apcupsd.DOMAIN] - SENSOR_PREFIX = 'UPS ' SENSOR_TYPES = { 'alarmdel': ['Alarm Delay', '', 'mdi:alarm'], diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index beba17ee2ea03b..0e860854af4ca7 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -33,8 +33,6 @@ ATTR_VERSION = 'version' DOMAIN = 'api' -DEPENDENCIES = ['http'] - STREAM_PING_PAYLOAD = 'ping' STREAM_PING_INTERVAL = 50 # seconds diff --git a/homeassistant/components/apns/notify.py b/homeassistant/components/apns/notify.py index d7f6559fe7eaad..365bdbcb4f557e 100644 --- a/homeassistant/components/apns/notify.py +++ b/homeassistant/components/apns/notify.py @@ -13,8 +13,6 @@ from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['apns2==0.3.0'] - APNS_DEVICES = 'apns.yaml' CONF_CERTFILE = 'cert_file' CONF_TOPIC = 'topic' diff --git a/homeassistant/components/apple_tv/__init__.py b/homeassistant/components/apple_tv/__init__.py index b265dc533eb85c..0ebe29ed47c9b4 100644 --- a/homeassistant/components/apple_tv/__init__.py +++ b/homeassistant/components/apple_tv/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyatv==0.3.12'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'apple_tv' diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index e00ce6ed13bcd2..9698ef4c704a47 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -14,8 +14,6 @@ from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES -DEPENDENCIES = ['apple_tv'] - _LOGGER = logging.getLogger(__name__) SUPPORT_APPLE_TV = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | \ diff --git a/homeassistant/components/apple_tv/remote.py b/homeassistant/components/apple_tv/remote.py index 25b500ac09d6ea..2839e3a5324c81 100644 --- a/homeassistant/components/apple_tv/remote.py +++ b/homeassistant/components/apple_tv/remote.py @@ -4,8 +4,6 @@ from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV -DEPENDENCIES = ['apple_tv'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/aqualogic/__init__.py b/homeassistant/components/aqualogic/__init__.py index a4f83b573f73b7..65718463218859 100644 --- a/homeassistant/components/aqualogic/__init__.py +++ b/homeassistant/components/aqualogic/__init__.py @@ -11,8 +11,6 @@ EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ["aqualogic==1.0"] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'aqualogic' diff --git a/homeassistant/components/aqualogic/sensor.py b/homeassistant/components/aqualogic/sensor.py index dc06a2127e941d..454cdbd7f6b505 100644 --- a/homeassistant/components/aqualogic/sensor.py +++ b/homeassistant/components/aqualogic/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['aqualogic'] - TEMP_UNITS = [TEMP_CELSIUS, TEMP_FAHRENHEIT] PERCENT_UNITS = ['%', '%'] SALT_UNITS = ['g/L', 'PPM'] diff --git a/homeassistant/components/aqualogic/switch.py b/homeassistant/components/aqualogic/switch.py index 21e573f944b6d2..b8bd8e41244c79 100644 --- a/homeassistant/components/aqualogic/switch.py +++ b/homeassistant/components/aqualogic/switch.py @@ -10,8 +10,6 @@ from . import DOMAIN, UPDATE_TOPIC -DEPENDENCIES = ['aqualogic'] - _LOGGER = logging.getLogger(__name__) SWITCH_TYPES = { diff --git a/homeassistant/components/aquostv/media_player.py b/homeassistant/components/aquostv/media_player.py index 0ffe48d21ec61f..a4e88f02a59fbe 100644 --- a/homeassistant/components/aquostv/media_player.py +++ b/homeassistant/components/aquostv/media_player.py @@ -15,8 +15,6 @@ CONF_USERNAME, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['sharp_aquos_rc==0.3.2'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Sharp Aquos TV' diff --git a/homeassistant/components/arduino/__init__.py b/homeassistant/components/arduino/__init__.py index 351122e74f0e0f..a6841e075643ef 100644 --- a/homeassistant/components/arduino/__init__.py +++ b/homeassistant/components/arduino/__init__.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_PORT import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['PyMata==2.14'] - _LOGGER = logging.getLogger(__name__) BOARD = None diff --git a/homeassistant/components/arduino/sensor.py b/homeassistant/components/arduino/sensor.py index ff758ea58470b5..0cc6e006b890c0 100644 --- a/homeassistant/components/arduino/sensor.py +++ b/homeassistant/components/arduino/sensor.py @@ -14,8 +14,6 @@ CONF_PINS = 'pins' CONF_TYPE = 'analog' -DEPENDENCIES = ['arduino'] - PIN_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.string, }) diff --git a/homeassistant/components/arduino/switch.py b/homeassistant/components/arduino/switch.py index 947c5188766cd1..92e91196a9aff5 100644 --- a/homeassistant/components/arduino/switch.py +++ b/homeassistant/components/arduino/switch.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['arduino'] - _LOGGER = logging.getLogger(__name__) CONF_PINS = 'pins' diff --git a/homeassistant/components/arlo/__init__.py b/homeassistant/components/arlo/__init__.py index cbb720778e5e7e..38230c2f05fe80 100644 --- a/homeassistant/components/arlo/__init__.py +++ b/homeassistant/components/arlo/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.helpers.dispatcher import dispatcher_send -REQUIREMENTS = ['pyarlo==0.2.3'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by arlo.netgear.com" diff --git a/homeassistant/components/arlo/alarm_control_panel.py b/homeassistant/components/arlo/alarm_control_panel.py index 3557ed125c6cda..a7addfb86eac7b 100644 --- a/homeassistant/components/arlo/alarm_control_panel.py +++ b/homeassistant/components/arlo/alarm_control_panel.py @@ -22,8 +22,6 @@ CONF_AWAY_MODE_NAME = 'away_mode_name' CONF_NIGHT_MODE_NAME = 'night_mode_name' -DEPENDENCIES = ['arlo'] - DISARMED = 'disarmed' ICON = 'mdi:security' diff --git a/homeassistant/components/arlo/camera.py b/homeassistant/components/arlo/camera.py index d4b00f0062503a..166e0781044c19 100644 --- a/homeassistant/components/arlo/camera.py +++ b/homeassistant/components/arlo/camera.py @@ -13,8 +13,6 @@ from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO -DEPENDENCIES = ['arlo', 'ffmpeg'] - _LOGGER = logging.getLogger(__name__) ARLO_MODE_ARMED = 'armed' diff --git a/homeassistant/components/arlo/sensor.py b/homeassistant/components/arlo/sensor.py index e08669eb80b60e..f83caec386b843 100644 --- a/homeassistant/components/arlo/sensor.py +++ b/homeassistant/components/arlo/sensor.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['arlo'] - # sensor_type [ description, unit, icon ] SENSOR_TYPES = { 'last_capture': ['Last', None, 'run-fast'], diff --git a/homeassistant/components/aruba/device_tracker.py b/homeassistant/components/aruba/device_tracker.py index ed1fee25a6c837..cde144e68f6924 100644 --- a/homeassistant/components/aruba/device_tracker.py +++ b/homeassistant/components/aruba/device_tracker.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pexpect==4.6.0'] - _DEVICES_REGEX = re.compile( r'(?P([^\s]+)?)\s+' + r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3})\s+' + diff --git a/homeassistant/components/arwn/sensor.py b/homeassistant/components/arwn/sensor.py index aef43c4b401556..94b552c6eba7e5 100644 --- a/homeassistant/components/arwn/sensor.py +++ b/homeassistant/components/arwn/sensor.py @@ -10,7 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] DOMAIN = 'arwn' DATA_ARWN = 'arwn' diff --git a/homeassistant/components/asterisk_cdr/mailbox.py b/homeassistant/components/asterisk_cdr/mailbox.py index db5d4e8d6eef12..647067b60d46d6 100644 --- a/homeassistant/components/asterisk_cdr/mailbox.py +++ b/homeassistant/components/asterisk_cdr/mailbox.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['asterisk_mbox'] - MAILBOX_NAME = 'asterisk_cdr' diff --git a/homeassistant/components/asterisk_mbox/__init__.py b/homeassistant/components/asterisk_mbox/__init__.py index d8d3b194cd7f16..a354226bbc06ab 100644 --- a/homeassistant/components/asterisk_mbox/__init__.py +++ b/homeassistant/components/asterisk_mbox/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, dispatcher_connect) -REQUIREMENTS = ['asterisk_mbox==0.5.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'asterisk_mbox' diff --git a/homeassistant/components/asterisk_mbox/mailbox.py b/homeassistant/components/asterisk_mbox/mailbox.py index a3e7c3f4d61c13..f79c8922214f57 100644 --- a/homeassistant/components/asterisk_mbox/mailbox.py +++ b/homeassistant/components/asterisk_mbox/mailbox.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['asterisk_mbox'] - SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request' SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated' diff --git a/homeassistant/components/asuswrt/__init__.py b/homeassistant/components/asuswrt/__init__.py index 9b004b5bc04fc4..cc51a15f8e8715 100644 --- a/homeassistant/components/asuswrt/__init__.py +++ b/homeassistant/components/asuswrt/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['aioasuswrt==1.1.21'] - _LOGGER = logging.getLogger(__name__) CONF_PUB_KEY = 'pub_key' diff --git a/homeassistant/components/asuswrt/device_tracker.py b/homeassistant/components/asuswrt/device_tracker.py index d115e640ffa863..68641f670aa267 100644 --- a/homeassistant/components/asuswrt/device_tracker.py +++ b/homeassistant/components/asuswrt/device_tracker.py @@ -5,8 +5,6 @@ from . import DATA_ASUSWRT -DEPENDENCIES = ['asuswrt'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/asuswrt/sensor.py b/homeassistant/components/asuswrt/sensor.py index ac80a447e28716..8ae629bd12d98b 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -5,8 +5,6 @@ from . import DATA_ASUSWRT -DEPENDENCIES = ['asuswrt'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 8e749dca46e5f7..e18c25706c1708 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -15,8 +15,6 @@ _CONFIGURING = {} -REQUIREMENTS = ['py-august==0.7.0'] - DEFAULT_TIMEOUT = 10 ACTIVITY_FETCH_LIMIT = 10 ACTIVITY_INITIAL_FETCH_LIMIT = 20 diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index 3a69d41177d6c5..d1f696458029d6 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['august'] - SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index 53a9d78bc60419..0bf8a28f904889 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -7,8 +7,6 @@ from . import DATA_AUGUST, DEFAULT_TIMEOUT -DEPENDENCIES = ['august'] - SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index e112eaa2592c31..5ad2bdc3b5bc25 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['august'] - SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/auth/__init__.py b/homeassistant/components/auth/__init__.py index d0157158aca6b2..f1deaf0cb856f4 100644 --- a/homeassistant/components/auth/__init__.py +++ b/homeassistant/components/auth/__init__.py @@ -138,8 +138,6 @@ from . import mfa_setup_flow DOMAIN = 'auth' -DEPENDENCIES = ['http'] - WS_TYPE_CURRENT_USER = 'auth/current_user' SCHEMA_WS_CURRENT_USER = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ vol.Required('type'): WS_TYPE_CURRENT_USER, diff --git a/homeassistant/components/automatic/device_tracker.py b/homeassistant/components/automatic/device_tracker.py index 8abd81e63bea6d..04e069a04f97b1 100644 --- a/homeassistant/components/automatic/device_tracker.py +++ b/homeassistant/components/automatic/device_tracker.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['aioautomatic==0.6.5'] - _LOGGER = logging.getLogger(__name__) ATTR_FUEL_LEVEL = 'fuel_level' @@ -34,8 +32,6 @@ DATA_REFRESH_TOKEN = 'refresh_token' DEFAULT_SCOPE = ['location', 'trip', 'vehicle:events', 'vehicle:profile'] DEFAULT_TIMEOUT = 5 -DEPENDENCIES = ['http'] - EVENT_AUTOMATIC_UPDATE = 'automatic_update' FULL_SCOPE = DEFAULT_SCOPE + ['current_location'] diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index b1470582d59232..fa8b77da768dc2 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -21,7 +21,6 @@ from homeassistant.util.dt import utcnow DOMAIN = 'automation' -DEPENDENCIES = ['group'] ENTITY_ID_FORMAT = DOMAIN + '.{}' GROUP_NAME_ALL_AUTOMATIONS = 'all automations' diff --git a/homeassistant/components/automation/litejet.py b/homeassistant/components/automation/litejet.py index 20c689d74cf438..51ec5baccfd4aa 100644 --- a/homeassistant/components/automation/litejet.py +++ b/homeassistant/components/automation/litejet.py @@ -9,8 +9,6 @@ import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_point_in_utc_time -DEPENDENCIES = ['litejet'] - _LOGGER = logging.getLogger(__name__) CONF_NUMBER = 'number' diff --git a/homeassistant/components/automation/mqtt.py b/homeassistant/components/automation/mqtt.py index ff89cd47024c16..837a22362b51d1 100644 --- a/homeassistant/components/automation/mqtt.py +++ b/homeassistant/components/automation/mqtt.py @@ -8,8 +8,6 @@ from homeassistant.const import (CONF_PLATFORM, CONF_PAYLOAD) import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['mqtt'] - CONF_ENCODING = 'encoding' CONF_TOPIC = 'topic' DEFAULT_ENCODING = 'utf-8' diff --git a/homeassistant/components/avion/light.py b/homeassistant/components/avion/light.py index 65172025b56b51..b138b8bf61f4b0 100644 --- a/homeassistant/components/avion/light.py +++ b/homeassistant/components/avion/light.py @@ -12,8 +12,6 @@ CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['avion==0.10'] - _LOGGER = logging.getLogger(__name__) SUPPORT_AVION_LED = SUPPORT_BRIGHTNESS diff --git a/homeassistant/components/awair/sensor.py b/homeassistant/components/awair/sensor.py index 7fdcc6735495c1..85f18e87d13f86 100644 --- a/homeassistant/components/awair/sensor.py +++ b/homeassistant/components/awair/sensor.py @@ -15,8 +15,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle, dt -REQUIREMENTS = ['python_awair==0.0.4'] - _LOGGER = logging.getLogger(__name__) ATTR_SCORE = 'score' diff --git a/homeassistant/components/aws/__init__.py b/homeassistant/components/aws/__init__.py index a15e56e9de89cd..9533d2c776d157 100644 --- a/homeassistant/components/aws/__init__.py +++ b/homeassistant/components/aws/__init__.py @@ -26,8 +26,6 @@ DOMAIN, ) -REQUIREMENTS = ["aiobotocore==0.10.2"] - _LOGGER = logging.getLogger(__name__) AWS_CREDENTIAL_SCHEMA = vol.Schema( diff --git a/homeassistant/components/aws/notify.py b/homeassistant/components/aws/notify.py index 48b80b64ce2ee6..3a6193f403d936 100644 --- a/homeassistant/components/aws/notify.py +++ b/homeassistant/components/aws/notify.py @@ -21,8 +21,6 @@ DATA_SESSIONS, ) -DEPENDENCIES = ["aws"] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/axis/__init__.py b/homeassistant/components/axis/__init__.py index e9ed37477a5a02..0cfa8923682d8b 100644 --- a/homeassistant/components/axis/__init__.py +++ b/homeassistant/components/axis/__init__.py @@ -11,8 +11,6 @@ from .const import CONF_CAMERA, CONF_EVENTS, DEFAULT_TRIGGER_TIME, DOMAIN from .device import AxisNetworkDevice, get_device -REQUIREMENTS = ['axis==19'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: cv.schema_with_slug_keys(DEVICE_SCHEMA), }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index 30e0e759a2cda3..c4393380351dc3 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -11,8 +11,6 @@ from .const import DOMAIN as AXIS_DOMAIN, LOGGER -DEPENDENCIES = [AXIS_DOMAIN] - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Axis binary sensor.""" diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index 62b694a99bb971..11368339e0dfae 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -11,8 +11,6 @@ from .const import DOMAIN as AXIS_DOMAIN -DEPENDENCIES = [AXIS_DOMAIN] - AXIS_IMAGE = 'http://{}:{}/axis-cgi/jpg/image.cgi' AXIS_VIDEO = 'http://{}:{}/axis-cgi/mjpg/video.cgi' AXIS_STREAM = 'rtsp://{}:{}@{}/axis-media/media.amp?videocodec=h264' diff --git a/homeassistant/components/baidu/tts.py b/homeassistant/components/baidu/tts.py index fbe27591ef535c..faf62e92651e5f 100644 --- a/homeassistant/components/baidu/tts.py +++ b/homeassistant/components/baidu/tts.py @@ -7,8 +7,6 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ["baidu-aip==1.6.6"] - _LOGGER = logging.getLogger(__name__) SUPPORTED_LANGUAGES = ['zh'] diff --git a/homeassistant/components/bbb_gpio/__init__.py b/homeassistant/components/bbb_gpio/__init__.py index 7749af8f335c48..85ea5753739bb0 100644 --- a/homeassistant/components/bbb_gpio/__init__.py +++ b/homeassistant/components/bbb_gpio/__init__.py @@ -4,8 +4,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['Adafruit_BBIO==1.0.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'bbb_gpio' diff --git a/homeassistant/components/bbb_gpio/binary_sensor.py b/homeassistant/components/bbb_gpio/binary_sensor.py index 1ee371dcc2a6ac..bcc45a4af32027 100644 --- a/homeassistant/components/bbb_gpio/binary_sensor.py +++ b/homeassistant/components/bbb_gpio/binary_sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['bbb_gpio'] - CONF_PINS = 'pins' CONF_BOUNCETIME = 'bouncetime' CONF_INVERT_LOGIC = 'invert_logic' diff --git a/homeassistant/components/bbb_gpio/switch.py b/homeassistant/components/bbb_gpio/switch.py index 3ad46fd61aedfd..49b4c5de19cc04 100644 --- a/homeassistant/components/bbb_gpio/switch.py +++ b/homeassistant/components/bbb_gpio/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['bbb_gpio'] - CONF_PINS = 'pins' CONF_INITIAL = 'initial' CONF_INVERT_LOGIC = 'invert_logic' diff --git a/homeassistant/components/bbox/device_tracker.py b/homeassistant/components/bbox/device_tracker.py index badbcdc8a0bf56..f70969aa61bc9b 100644 --- a/homeassistant/components/bbox/device_tracker.py +++ b/homeassistant/components/bbox/device_tracker.py @@ -12,8 +12,6 @@ from homeassistant.util import Throttle import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pybbox==0.0.5-alpha'] - _LOGGER = logging.getLogger(__name__) DEFAULT_HOST = '192.168.1.254' diff --git a/homeassistant/components/bbox/sensor.py b/homeassistant/components/bbox/sensor.py index 5b3c31d1ddf241..80fa82b30fc7f1 100644 --- a/homeassistant/components/bbox/sensor.py +++ b/homeassistant/components/bbox/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pybbox==0.0.5-alpha'] - _LOGGER = logging.getLogger(__name__) BANDWIDTH_MEGABITS_SECONDS = 'Mb/s' # type: str diff --git a/homeassistant/components/bh1750/sensor.py b/homeassistant/components/bh1750/sensor.py index e30eededa51b28..eaee023ce8616b 100644 --- a/homeassistant/components/bh1750/sensor.py +++ b/homeassistant/components/bh1750/sensor.py @@ -9,9 +9,6 @@ from homeassistant.const import CONF_NAME, DEVICE_CLASS_ILLUMINANCE from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['i2csense==0.0.4', - 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) CONF_I2C_ADDRESS = 'i2c_address' diff --git a/homeassistant/components/bitcoin/sensor.py b/homeassistant/components/bitcoin/sensor.py index 3bc14637a87631..6ccb10f50e6115 100644 --- a/homeassistant/components/bitcoin/sensor.py +++ b/homeassistant/components/bitcoin/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['blockchain==1.4.4'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by blockchain.info" diff --git a/homeassistant/components/blackbird/media_player.py b/homeassistant/components/blackbird/media_player.py index c66bc412160f0e..be0538a89e94d7 100644 --- a/homeassistant/components/blackbird/media_player.py +++ b/homeassistant/components/blackbird/media_player.py @@ -14,8 +14,6 @@ STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyblackbird==0.5'] - _LOGGER = logging.getLogger(__name__) SUPPORT_BLACKBIRD = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE diff --git a/homeassistant/components/blink/__init__.py b/homeassistant/components/blink/__init__.py index 488209e3689fc3..397ee097cae24e 100644 --- a/homeassistant/components/blink/__init__.py +++ b/homeassistant/components/blink/__init__.py @@ -10,8 +10,6 @@ CONF_BINARY_SENSORS, CONF_SENSORS, CONF_FILENAME, CONF_MONITORED_CONDITIONS, TEMP_FAHRENHEIT) -REQUIREMENTS = ['blinkpy==0.13.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'blink' diff --git a/homeassistant/components/blink/alarm_control_panel.py b/homeassistant/components/blink/alarm_control_panel.py index 75e645dff5f3f9..8cc89d90b2f889 100644 --- a/homeassistant/components/blink/alarm_control_panel.py +++ b/homeassistant/components/blink/alarm_control_panel.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['blink'] - ICON = 'mdi:security' diff --git a/homeassistant/components/blink/binary_sensor.py b/homeassistant/components/blink/binary_sensor.py index 466b73caf5fd5a..4c268989d32bac 100644 --- a/homeassistant/components/blink/binary_sensor.py +++ b/homeassistant/components/blink/binary_sensor.py @@ -4,8 +4,6 @@ from . import BINARY_SENSORS, BLINK_DATA -DEPENDENCIES = ['blink'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the blink binary sensors.""" diff --git a/homeassistant/components/blink/camera.py b/homeassistant/components/blink/camera.py index 1da3080e3ff074..d1301319a81263 100644 --- a/homeassistant/components/blink/camera.py +++ b/homeassistant/components/blink/camera.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['blink'] - ATTR_VIDEO_CLIP = 'video' ATTR_IMAGE = 'image' diff --git a/homeassistant/components/blink/sensor.py b/homeassistant/components/blink/sensor.py index 0e97db9d7d410d..6fb8be8e4ea71f 100644 --- a/homeassistant/components/blink/sensor.py +++ b/homeassistant/components/blink/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['blink'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a Blink sensor.""" diff --git a/homeassistant/components/blinksticklight/light.py b/homeassistant/components/blinksticklight/light.py index 0d4c7b736f3d3d..8eab6afaeb7289 100644 --- a/homeassistant/components/blinksticklight/light.py +++ b/homeassistant/components/blinksticklight/light.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['blinkstick==1.1.8'] - _LOGGER = logging.getLogger(__name__) CONF_SERIAL = 'serial' diff --git a/homeassistant/components/blinkt/light.py b/homeassistant/components/blinkt/light.py index 57d19172614c9f..cb3e854b3888a9 100644 --- a/homeassistant/components/blinkt/light.py +++ b/homeassistant/components/blinkt/light.py @@ -11,8 +11,6 @@ from homeassistant.const import CONF_NAME import homeassistant.util.color as color_util -REQUIREMENTS = ['blinkt==0.1.0'] - _LOGGER = logging.getLogger(__name__) SUPPORT_BLINKT = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/blockchain/sensor.py b/homeassistant/components/blockchain/sensor.py index def1dc3309f9a9..436e2979a6e06a 100644 --- a/homeassistant/components/blockchain/sensor.py +++ b/homeassistant/components/blockchain/sensor.py @@ -9,8 +9,6 @@ from homeassistant.const import (CONF_NAME, ATTR_ATTRIBUTION) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-blockchain-api==0.0.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by blockchain.info" diff --git a/homeassistant/components/bloomsky/binary_sensor.py b/homeassistant/components/bloomsky/binary_sensor.py index 8d4a89a017908c..b17c4e4c25781e 100644 --- a/homeassistant/components/bloomsky/binary_sensor.py +++ b/homeassistant/components/bloomsky/binary_sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['bloomsky'] - SENSOR_TYPES = { 'Rain': 'moisture', 'Night': None, diff --git a/homeassistant/components/bloomsky/camera.py b/homeassistant/components/bloomsky/camera.py index a2e1d8e2d3a59d..a748ff2b5b8905 100644 --- a/homeassistant/components/bloomsky/camera.py +++ b/homeassistant/components/bloomsky/camera.py @@ -7,8 +7,6 @@ from . import BLOOMSKY -DEPENDENCIES = ['bloomsky'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up access to BloomSky cameras.""" diff --git a/homeassistant/components/bloomsky/sensor.py b/homeassistant/components/bloomsky/sensor.py index 6909c57eec4e87..e7d4bc5c8eb0e5 100644 --- a/homeassistant/components/bloomsky/sensor.py +++ b/homeassistant/components/bloomsky/sensor.py @@ -12,8 +12,6 @@ LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['bloomsky'] - # These are the available sensors SENSOR_TYPES = ['Temperature', 'Humidity', diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index c4cd3572e75c3b..080afeea280426 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -29,8 +29,6 @@ from homeassistant.util import Throttle import homeassistant.util.dt as dt_util -REQUIREMENTS = ['xmltodict==0.11.0'] - _LOGGER = logging.getLogger(__name__) ATTR_MASTER = 'master' diff --git a/homeassistant/components/bluetooth_le_tracker/device_tracker.py b/homeassistant/components/bluetooth_le_tracker/device_tracker.py index dfb5fa073b93de..f1aab4e1fd5a34 100644 --- a/homeassistant/components/bluetooth_le_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_le_tracker/device_tracker.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pygatt[GATTTOOL]==3.2.0'] - BLE_PREFIX = 'BLE_' MIN_SEEN_NEW = 5 diff --git a/homeassistant/components/bluetooth_tracker/device_tracker.py b/homeassistant/components/bluetooth_tracker/device_tracker.py index 3a4aa8880012d1..d464e87ce640fe 100644 --- a/homeassistant/components/bluetooth_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_tracker/device_tracker.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pybluez==0.22', 'bt_proximity==0.1.2'] - BT_PREFIX = 'BT_' CONF_REQUEST_RSSI = 'request_rssi' diff --git a/homeassistant/components/bme280/sensor.py b/homeassistant/components/bme280/sensor.py index 73982ecc628bb0..66b4ba672589ad 100644 --- a/homeassistant/components/bme280/sensor.py +++ b/homeassistant/components/bme280/sensor.py @@ -13,9 +13,6 @@ from homeassistant.util import Throttle from homeassistant.util.temperature import celsius_to_fahrenheit -REQUIREMENTS = ['i2csense==0.0.4', - 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) CONF_I2C_ADDRESS = 'i2c_address' diff --git a/homeassistant/components/bme680/sensor.py b/homeassistant/components/bme680/sensor.py index 8f515cc469a265..73fe827be6ba20 100644 --- a/homeassistant/components/bme680/sensor.py +++ b/homeassistant/components/bme680/sensor.py @@ -13,9 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util.temperature import celsius_to_fahrenheit -REQUIREMENTS = ['bme680==1.0.5', - 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) CONF_I2C_ADDRESS = 'i2c_address' diff --git a/homeassistant/components/bmw_connected_drive/__init__.py b/homeassistant/components/bmw_connected_drive/__init__.py index e1ac30120d2115..10c58696740043 100644 --- a/homeassistant/components/bmw_connected_drive/__init__.py +++ b/homeassistant/components/bmw_connected_drive/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers.event import track_utc_time_change import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['bimmer_connected==0.5.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'bmw_connected_drive' diff --git a/homeassistant/components/bmw_connected_drive/binary_sensor.py b/homeassistant/components/bmw_connected_drive/binary_sensor.py index deab157292d2f3..8769fcf7d6205b 100644 --- a/homeassistant/components/bmw_connected_drive/binary_sensor.py +++ b/homeassistant/components/bmw_connected_drive/binary_sensor.py @@ -6,8 +6,6 @@ from . import DOMAIN as BMW_DOMAIN -DEPENDENCIES = ['bmw_connected_drive'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { diff --git a/homeassistant/components/bmw_connected_drive/device_tracker.py b/homeassistant/components/bmw_connected_drive/device_tracker.py index 20e84e33e29bd3..229488186ae16a 100644 --- a/homeassistant/components/bmw_connected_drive/device_tracker.py +++ b/homeassistant/components/bmw_connected_drive/device_tracker.py @@ -5,8 +5,6 @@ from . import DOMAIN as BMW_DOMAIN -DEPENDENCIES = ['bmw_connected_drive'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/lock.py b/homeassistant/components/bmw_connected_drive/lock.py index fe646dcd1c9c10..455e1427b05bae 100644 --- a/homeassistant/components/bmw_connected_drive/lock.py +++ b/homeassistant/components/bmw_connected_drive/lock.py @@ -6,8 +6,6 @@ from . import DOMAIN as BMW_DOMAIN -DEPENDENCIES = ['bmw_connected_drive'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bmw_connected_drive/sensor.py b/homeassistant/components/bmw_connected_drive/sensor.py index 03c03f01b4a148..4d8b7adde1b5bb 100644 --- a/homeassistant/components/bmw_connected_drive/sensor.py +++ b/homeassistant/components/bmw_connected_drive/sensor.py @@ -9,8 +9,6 @@ from . import DOMAIN as BMW_DOMAIN -DEPENDENCIES = ['bmw_connected_drive'] - _LOGGER = logging.getLogger(__name__) ATTR_TO_HA_METRIC = { diff --git a/homeassistant/components/bom/camera.py b/homeassistant/components/bom/camera.py index d3e78034015bc1..87ffd4ab791b94 100644 --- a/homeassistant/components/bom/camera.py +++ b/homeassistant/components/bom/camera.py @@ -5,8 +5,6 @@ from homeassistant.const import CONF_ID, CONF_NAME from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['bomradarloop==0.1.2'] - CONF_DELTA = 'delta' CONF_FRAMES = 'frames' CONF_LOCATION = 'location' diff --git a/homeassistant/components/braviatv/media_player.py b/homeassistant/components/braviatv/media_player.py index 45fdb63a4a9b45..6377561009d58b 100644 --- a/homeassistant/components/braviatv/media_player.py +++ b/homeassistant/components/braviatv/media_player.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['braviarc-homeassistant==0.3.7.dev0'] - BRAVIA_CONFIG_FILE = 'bravia.conf' CLIENTID_PREFIX = 'HomeAssistant' diff --git a/homeassistant/components/broadlink/sensor.py b/homeassistant/components/broadlink/sensor.py index b3ce245a979ae7..c542d8f5549e25 100644 --- a/homeassistant/components/broadlink/sensor.py +++ b/homeassistant/components/broadlink/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['broadlink==0.9.0'] - _LOGGER = logging.getLogger(__name__) DEVICE_DEFAULT_NAME = 'Broadlink sensor' diff --git a/homeassistant/components/broadlink/switch.py b/homeassistant/components/broadlink/switch.py index 8695f70786c6f2..f2f7b4a5d957f8 100644 --- a/homeassistant/components/broadlink/switch.py +++ b/homeassistant/components/broadlink/switch.py @@ -17,8 +17,6 @@ from homeassistant.util import Throttle, slugify from homeassistant.util.dt import utcnow -REQUIREMENTS = ['broadlink==0.9.0'] - _LOGGER = logging.getLogger(__name__) TIME_BETWEEN_UPDATES = timedelta(seconds=5) diff --git a/homeassistant/components/brottsplatskartan/sensor.py b/homeassistant/components/brottsplatskartan/sensor.py index f990dd1aba1630..c36c5c0ad1c4a3 100644 --- a/homeassistant/components/brottsplatskartan/sensor.py +++ b/homeassistant/components/brottsplatskartan/sensor.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['brottsplatskartan==0.0.1'] - _LOGGER = logging.getLogger(__name__) CONF_AREA = 'area' diff --git a/homeassistant/components/brunt/cover.py b/homeassistant/components/brunt/cover.py index dc17cebcec2fd6..f9455ae0910934 100644 --- a/homeassistant/components/brunt/cover.py +++ b/homeassistant/components/brunt/cover.py @@ -13,8 +13,6 @@ ) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['brunt==0.1.3'] - _LOGGER = logging.getLogger(__name__) COVER_FEATURES = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION diff --git a/homeassistant/components/bt_home_hub_5/device_tracker.py b/homeassistant/components/bt_home_hub_5/device_tracker.py index 61853c0af89f0b..65f88e05d1cc1d 100644 --- a/homeassistant/components/bt_home_hub_5/device_tracker.py +++ b/homeassistant/components/bt_home_hub_5/device_tracker.py @@ -8,8 +8,6 @@ DeviceScanner) from homeassistant.const import CONF_HOST -REQUIREMENTS = ['bthomehub5-devicelist==0.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_DEFAULT_IP = '192.168.1.254' diff --git a/homeassistant/components/bt_smarthub/device_tracker.py b/homeassistant/components/bt_smarthub/device_tracker.py index 5820feda567903..adc873f56b3965 100644 --- a/homeassistant/components/bt_smarthub/device_tracker.py +++ b/homeassistant/components/bt_smarthub/device_tracker.py @@ -8,8 +8,6 @@ DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOST -REQUIREMENTS = ['btsmarthub_devicelist==0.1.3'] - _LOGGER = logging.getLogger(__name__) CONF_DEFAULT_IP = '192.168.1.254' diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index 754873fa2c914f..f3aaa9b75378dc 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -17,8 +17,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util import dt as dt_util -REQUIREMENTS = ['buienradar==0.91'] - _LOGGER = logging.getLogger(__name__) MEASURED_LABEL = 'Measured' diff --git a/homeassistant/components/buienradar/weather.py b/homeassistant/components/buienradar/weather.py index 86dcb229a78bbd..7d77bec7cca05a 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -13,8 +13,6 @@ # Reuse data and API logic from the sensor implementation from .sensor import BrData -REQUIREMENTS = ['buienradar==0.91'] - _LOGGER = logging.getLogger(__name__) DATA_CONDITION = 'buienradar_condition' diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index 65cb20811b880b..446473c7f40db9 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle, dt -REQUIREMENTS = ['caldav==0.5.0'] - _LOGGER = logging.getLogger(__name__) CONF_DEVICE_ID = 'device_id' diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index aa9e3153fe5bbd..73a779816a3d1c 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -22,8 +22,6 @@ DOMAIN = 'calendar' -DEPENDENCIES = ['http'] - ENTITY_ID_FORMAT = DOMAIN + '.{}' SCAN_INTERVAL = timedelta(seconds=60) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 2ddab537acc09e..1287de92ffd2fd 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -36,8 +36,6 @@ from .const import DOMAIN, DATA_CAMERA_PREFS from .prefs import CameraPreferences -DEPENDENCIES = ['http'] - _LOGGER = logging.getLogger(__name__) SERVICE_ENABLE_MOTION = 'enable_motion_detection' diff --git a/homeassistant/components/canary/__init__.py b/homeassistant/components/canary/__init__.py index e53c7e22d2d6dd..52b38f14795578 100644 --- a/homeassistant/components/canary/__init__.py +++ b/homeassistant/components/canary/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers import discovery from homeassistant.util import Throttle -REQUIREMENTS = ['py-canary==0.5.0'] - _LOGGER = logging.getLogger(__name__) NOTIFICATION_ID = 'canary_notification' diff --git a/homeassistant/components/canary/alarm_control_panel.py b/homeassistant/components/canary/alarm_control_panel.py index faa7d819a2e91f..7402d7855324b8 100644 --- a/homeassistant/components/canary/alarm_control_panel.py +++ b/homeassistant/components/canary/alarm_control_panel.py @@ -8,8 +8,6 @@ from . import DATA_CANARY -DEPENDENCIES = ['canary'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index fc740a46f628d9..33e1265921f199 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -13,8 +13,6 @@ from . import DATA_CANARY, DEFAULT_TIMEOUT -DEPENDENCIES = ['canary', 'ffmpeg'] - _LOGGER = logging.getLogger(__name__) CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' diff --git a/homeassistant/components/canary/sensor.py b/homeassistant/components/canary/sensor.py index fb3aaf78b0a487..220abc9b387259 100644 --- a/homeassistant/components/canary/sensor.py +++ b/homeassistant/components/canary/sensor.py @@ -6,8 +6,6 @@ from . import DATA_CANARY -DEPENDENCIES = ['canary'] - SENSOR_VALUE_PRECISION = 2 ATTR_AIR_QUALITY = "air_quality" diff --git a/homeassistant/components/cast/__init__.py b/homeassistant/components/cast/__init__.py index 0ec3ac150d7501..1a93020c22956c 100644 --- a/homeassistant/components/cast/__init__.py +++ b/homeassistant/components/cast/__init__.py @@ -2,8 +2,6 @@ from homeassistant import config_entries from homeassistant.helpers import config_entry_flow -REQUIREMENTS = ['pychromecast==3.2.0'] - DOMAIN = 'cast' diff --git a/homeassistant/components/channels/media_player.py b/homeassistant/components/channels/media_player.py index afe29ae079f9ac..abd3281d11a73a 100644 --- a/homeassistant/components/channels/media_player.py +++ b/homeassistant/components/channels/media_player.py @@ -15,8 +15,6 @@ STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pychannels==1.0.0'] - _LOGGER = logging.getLogger(__name__) DATA_CHANNELS = 'channels' diff --git a/homeassistant/components/cisco_ios/device_tracker.py b/homeassistant/components/cisco_ios/device_tracker.py index d5a64626e89924..5eb039709890cb 100644 --- a/homeassistant/components/cisco_ios/device_tracker.py +++ b/homeassistant/components/cisco_ios/device_tracker.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pexpect==4.6.0'] - PLATFORM_SCHEMA = vol.All( PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, diff --git a/homeassistant/components/cisco_mobility_express/device_tracker.py b/homeassistant/components/cisco_mobility_express/device_tracker.py index a722a994350041..4af94588d3b18f 100644 --- a/homeassistant/components/cisco_mobility_express/device_tracker.py +++ b/homeassistant/components/cisco_mobility_express/device_tracker.py @@ -9,10 +9,6 @@ from homeassistant.const import ( CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_SSL, CONF_VERIFY_SSL) - -REQUIREMENTS = ['ciscomobilityexpress==0.1.5'] - - _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = False diff --git a/homeassistant/components/cisco_webex_teams/notify.py b/homeassistant/components/cisco_webex_teams/notify.py index f893d4071b0e6e..22f8679f6184b0 100644 --- a/homeassistant/components/cisco_webex_teams/notify.py +++ b/homeassistant/components/cisco_webex_teams/notify.py @@ -8,8 +8,6 @@ from homeassistant.const import (CONF_TOKEN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['webexteamssdk==1.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_ROOM_ID = 'room_id' diff --git a/homeassistant/components/ciscospark/notify.py b/homeassistant/components/ciscospark/notify.py index 2eccb233a3cdd6..320c342b1433bb 100644 --- a/homeassistant/components/ciscospark/notify.py +++ b/homeassistant/components/ciscospark/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import (ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['ciscosparkapi==0.4.2'] - _LOGGER = logging.getLogger(__name__) CONF_ROOMID = 'roomid' diff --git a/homeassistant/components/clementine/media_player.py b/homeassistant/components/clementine/media_player.py index 65c6be19845faa..fc6e27be1bd61b 100644 --- a/homeassistant/components/clementine/media_player.py +++ b/homeassistant/components/clementine/media_player.py @@ -16,8 +16,6 @@ STATE_PAUSED, STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-clementine-remote==1.0.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Clementine Remote' diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 41045ba1f91265..ee0cd0c00909a5 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -24,9 +24,6 @@ CONF_USER_POOL_ID, DOMAIN, MODE_DEV, MODE_PROD) from .prefs import CloudPreferences -REQUIREMENTS = ['hass-nabucasa==0.11'] -DEPENDENCIES = ['http'] - _LOGGER = logging.getLogger(__name__) DEFAULT_MODE = MODE_PROD diff --git a/homeassistant/components/cloud/binary_sensor.py b/homeassistant/components/cloud/binary_sensor.py index 19a6528e3218f5..3e4aaf9cc848a6 100644 --- a/homeassistant/components/cloud/binary_sensor.py +++ b/homeassistant/components/cloud/binary_sensor.py @@ -6,8 +6,6 @@ from .const import DISPATCHER_REMOTE_UPDATE, DOMAIN -DEPENDENCIES = ['cloud'] - WAIT_UNTIL_CHANGE = 3 diff --git a/homeassistant/components/cloudflare/__init__.py b/homeassistant/components/cloudflare/__init__.py index 363e7c5eeb11dd..ce88f820fe372d 100644 --- a/homeassistant/components/cloudflare/__init__.py +++ b/homeassistant/components/cloudflare/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['pycfdns==0.0.1'] - _LOGGER = logging.getLogger(__name__) CONF_RECORDS = 'records' diff --git a/homeassistant/components/cmus/media_player.py b/homeassistant/components/cmus/media_player.py index e5134508feaaaa..4f1dfc5053642d 100644 --- a/homeassistant/components/cmus/media_player.py +++ b/homeassistant/components/cmus/media_player.py @@ -14,8 +14,6 @@ STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pycmus==0.1.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'cmus' diff --git a/homeassistant/components/co2signal/sensor.py b/homeassistant/components/co2signal/sensor.py index b9ae5e26ebef20..990521d041854d 100644 --- a/homeassistant/components/co2signal/sensor.py +++ b/homeassistant/components/co2signal/sensor.py @@ -11,8 +11,6 @@ CONF_COUNTRY_CODE = "country_code" -REQUIREMENTS = ['co2signal==0.4.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Data provided by CO2signal' diff --git a/homeassistant/components/coinbase/__init__.py b/homeassistant/components/coinbase/__init__.py index 40d04eadb3a79e..21efd5f9b8eccb 100644 --- a/homeassistant/components/coinbase/__init__.py +++ b/homeassistant/components/coinbase/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.util import Throttle -REQUIREMENTS = ['coinbase==2.1.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'coinbase' diff --git a/homeassistant/components/coinbase/sensor.py b/homeassistant/components/coinbase/sensor.py index 2483d46b38a095..9470999efbb93b 100644 --- a/homeassistant/components/coinbase/sensor.py +++ b/homeassistant/components/coinbase/sensor.py @@ -17,7 +17,6 @@ ATTRIBUTION = "Data provided by coinbase.com" DATA_COINBASE = 'coinbase_cache' -DEPENDENCIES = ['coinbase'] def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/coinmarketcap/sensor.py b/homeassistant/components/coinmarketcap/sensor.py index a39f11b5352a0a..4d8af5a721d1ff 100644 --- a/homeassistant/components/coinmarketcap/sensor.py +++ b/homeassistant/components/coinmarketcap/sensor.py @@ -11,8 +11,6 @@ ATTR_ATTRIBUTION, CONF_DISPLAY_CURRENCY) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['coinmarketcap==5.0.3'] - _LOGGER = logging.getLogger(__name__) ATTR_VOLUME_24H = 'volume_24h' diff --git a/homeassistant/components/comfoconnect/__init__.py b/homeassistant/components/comfoconnect/__init__.py index 64ebec18545903..3c50f3fb723887 100644 --- a/homeassistant/components/comfoconnect/__init__.py +++ b/homeassistant/components/comfoconnect/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import dispatcher_send -REQUIREMENTS = ['pycomfoconnect==0.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'comfoconnect' diff --git a/homeassistant/components/comfoconnect/fan.py b/homeassistant/components/comfoconnect/fan.py index 88dcffcfd21485..56175f0bca0963 100644 --- a/homeassistant/components/comfoconnect/fan.py +++ b/homeassistant/components/comfoconnect/fan.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['comfoconnect'] - SPEED_MAPPING = { 0: SPEED_OFF, 1: SPEED_LOW, diff --git a/homeassistant/components/comfoconnect/sensor.py b/homeassistant/components/comfoconnect/sensor.py index edb96b8d279d3d..db2a9393e2b481 100644 --- a/homeassistant/components/comfoconnect/sensor.py +++ b/homeassistant/components/comfoconnect/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['comfoconnect'] - SENSOR_TYPES = {} diff --git a/homeassistant/components/concord232/alarm_control_panel.py b/homeassistant/components/concord232/alarm_control_panel.py index a209fba93edfb3..c56e7e71531298 100644 --- a/homeassistant/components/concord232/alarm_control_panel.py +++ b/homeassistant/components/concord232/alarm_control_panel.py @@ -12,8 +12,6 @@ CONF_HOST, CONF_NAME, CONF_PORT, CONF_CODE, CONF_MODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) -REQUIREMENTS = ['concord232==0.15'] - _LOGGER = logging.getLogger(__name__) DEFAULT_HOST = 'localhost' diff --git a/homeassistant/components/concord232/binary_sensor.py b/homeassistant/components/concord232/binary_sensor.py index c1a31eb9ead989..ae464da97987e1 100644 --- a/homeassistant/components/concord232/binary_sensor.py +++ b/homeassistant/components/concord232/binary_sensor.py @@ -10,8 +10,6 @@ from homeassistant.const import (CONF_HOST, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['concord232==0.15'] - _LOGGER = logging.getLogger(__name__) CONF_EXCLUDE_ZONES = 'exclude_zones' diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py index 7807c52737091e..3752d5d37bf147 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -12,7 +12,6 @@ from homeassistant.util.yaml import load_yaml, dump DOMAIN = 'config' -DEPENDENCIES = ['http'] SECTIONS = ( 'area_registry', 'auth', diff --git a/homeassistant/components/conversation/__init__.py b/homeassistant/components/conversation/__init__.py index bb2d692f2490b3..bd577127fa0385 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -21,7 +21,6 @@ ATTR_TEXT = 'text' -DEPENDENCIES = ['http'] DOMAIN = 'conversation' REGEX_TURN_COMMAND = re.compile(r'turn (?P(?: |\w)+) (?P\w+)') diff --git a/homeassistant/components/coolmaster/climate.py b/homeassistant/components/coolmaster/climate.py index 77bb9a6b213d29..d6402bd893cabe 100644 --- a/homeassistant/components/coolmaster/climate.py +++ b/homeassistant/components/coolmaster/climate.py @@ -13,8 +13,6 @@ ATTR_TEMPERATURE, CONF_HOST, CONF_PORT, TEMP_CELSIUS, TEMP_FAHRENHEIT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pycoolmasternet==0.0.4'] - SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE | SUPPORT_OPERATION_MODE | SUPPORT_ON_OFF) diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 9bb1aacfaf13ea..8609d3c9cf6402 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -22,7 +22,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'cover' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=15) GROUP_NAME_ALL_COVERS = 'all covers' diff --git a/homeassistant/components/cppm_tracker/device_tracker.py b/homeassistant/components/cppm_tracker/device_tracker.py index 31d8122692a39e..608ce6dad6bcaa 100755 --- a/homeassistant/components/cppm_tracker/device_tracker.py +++ b/homeassistant/components/cppm_tracker/device_tracker.py @@ -11,8 +11,6 @@ CONF_HOST, CONF_API_KEY ) -REQUIREMENTS = ['clearpasspy==1.0.2'] - SCAN_INTERVAL = timedelta(seconds=120) CLIENT_ID = 'client_id' diff --git a/homeassistant/components/cpuspeed/sensor.py b/homeassistant/components/cpuspeed/sensor.py index 98d22c20d153b6..ef9cb218cd79b0 100644 --- a/homeassistant/components/cpuspeed/sensor.py +++ b/homeassistant/components/cpuspeed/sensor.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['py-cpuinfo==5.0.0'] - _LOGGER = logging.getLogger(__name__) ATTR_BRAND = 'Brand' diff --git a/homeassistant/components/crimereports/sensor.py b/homeassistant/components/crimereports/sensor.py index 139346755178e9..5e25d800247b7d 100644 --- a/homeassistant/components/crimereports/sensor.py +++ b/homeassistant/components/crimereports/sensor.py @@ -16,8 +16,6 @@ from homeassistant.util.dt import now import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['crimereports==1.0.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'crimereports' diff --git a/homeassistant/components/cups/sensor.py b/homeassistant/components/cups/sensor.py index 97f894aed86981..cf0ba5f7f8d3ee 100644 --- a/homeassistant/components/cups/sensor.py +++ b/homeassistant/components/cups/sensor.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pycups==1.9.73'] - _LOGGER = logging.getLogger(__name__) ATTR_DEVICE_URI = 'device_uri' diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 8e96ccb87388a9..fc15ebea772704 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -17,8 +17,6 @@ from . import config_flow # noqa pylint_disable=unused-import -REQUIREMENTS = ['pydaikin==1.4.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'daikin' diff --git a/homeassistant/components/danfoss_air/__init__.py b/homeassistant/components/danfoss_air/__init__.py index f4a7b92c17cbb9..a340b94e9a4618 100644 --- a/homeassistant/components/danfoss_air/__init__.py +++ b/homeassistant/components/danfoss_air/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['pydanfossair==0.0.7'] - _LOGGER = logging.getLogger(__name__) DANFOSS_AIR_PLATFORMS = ['sensor', 'binary_sensor', 'switch'] diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index 6aee3457acb381..63c2f782d17e0e 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['python-forecastio==1.4.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Powered by Dark Sky" diff --git a/homeassistant/components/darksky/weather.py b/homeassistant/components/darksky/weather.py index 5b3db4312bfb10..dd945e7b01c9ba 100644 --- a/homeassistant/components/darksky/weather.py +++ b/homeassistant/components/darksky/weather.py @@ -16,8 +16,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle from homeassistant.util.pressure import convert as convert_pressure -REQUIREMENTS = ['python-forecastio==1.4.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Powered by Dark Sky" diff --git a/homeassistant/components/datadog/__init__.py b/homeassistant/components/datadog/__init__.py index 3b519514d17b44..a59d828301c20f 100644 --- a/homeassistant/components/datadog/__init__.py +++ b/homeassistant/components/datadog/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import state as state_helper import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['datadog==0.15.0'] - _LOGGER = logging.getLogger(__name__) CONF_RATE = 'rate' diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index 807f82821fb293..153e654f3fb322 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -13,8 +13,6 @@ CONF_MASTER_GATEWAY, DEFAULT_PORT, DOMAIN, _LOGGER) from .gateway import DeconzGateway -REQUIREMENTS = ['pydeconz==54'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Optional(CONF_API_KEY): cv.string, diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index 70de1fd7cf44ba..fbb15abc744ade 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -8,8 +8,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - ATTR_ORIENTATION = 'orientation' ATTR_TILTANGLE = 'tiltangle' ATTR_VIBRATIONSTRENGTH = 'vibrationstrength' diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index c4327d3c497fec..c4a021a80c223c 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -11,8 +11,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the deCONZ climate devices. diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 903c1160eb8226..45a1b0c67e51b7 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -9,8 +9,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - ZIGBEE_SPEC = ['lumi.curtain'] diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index b5a2b075f75bdf..7514162fefad44 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -12,8 +12,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/deconz/scene.py b/homeassistant/components/deconz/scene.py index 1ae1e079daa065..d2e7f6719e915a 100644 --- a/homeassistant/components/deconz/scene.py +++ b/homeassistant/components/deconz/scene.py @@ -6,8 +6,6 @@ from .const import NEW_SCENE from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 7c3109e1f5961f..9f1e87db4ba821 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -9,8 +9,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - ATTR_CURRENT = 'current' ATTR_DAYLIGHT = 'daylight' ATTR_EVENT_ID = 'event_id' diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index b9f959766fc2c2..c399f5da128d6f 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -7,8 +7,6 @@ from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -DEPENDENCIES = ['deconz'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/decora/light.py b/homeassistant/components/decora/light.py index fc8b2859c0712d..2f6c050b79e64a 100644 --- a/homeassistant/components/decora/light.py +++ b/homeassistant/components/decora/light.py @@ -12,8 +12,6 @@ PLATFORM_SCHEMA) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['decora==0.6', 'bluepy==1.1.4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_DECORA_LED = (SUPPORT_BRIGHTNESS) diff --git a/homeassistant/components/decora_wifi/light.py b/homeassistant/components/decora_wifi/light.py index b7be6bffb0151a..390af765b62b41 100644 --- a/homeassistant/components/decora_wifi/light.py +++ b/homeassistant/components/decora_wifi/light.py @@ -12,8 +12,6 @@ EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['decora_wifi==1.3'] - _LOGGER = logging.getLogger(__name__) # Validation of the user's configuration diff --git a/homeassistant/components/default_config/__init__.py b/homeassistant/components/default_config/__init__.py index 6743893888d819..273513262d5f9b 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -5,26 +5,6 @@ av = None DOMAIN = 'default_config' -DEPENDENCIES = [ - 'automation', - 'cloud', - 'config', - 'conversation', - 'frontend', - 'history', - 'logbook', - 'map', - 'mobile_app', - 'person', - 'script', - 'sun', - 'system_health', - 'updater', - 'zeroconf', -] -# Only automatically set up the stream component when dependency installed -if av is not None: - DEPENDENCIES.append('stream') async def async_setup(hass, config): diff --git a/homeassistant/components/deluge/sensor.py b/homeassistant/components/deluge/sensor.py index 32b1c16a47c783..1002ae51077849 100644 --- a/homeassistant/components/deluge/sensor.py +++ b/homeassistant/components/deluge/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['deluge-client==1.4.0'] - _LOGGER = logging.getLogger(__name__) _THROTTLED_REFRESH = None diff --git a/homeassistant/components/deluge/switch.py b/homeassistant/components/deluge/switch.py index d7c60bd96e2932..d72ce9a53083b7 100644 --- a/homeassistant/components/deluge/switch.py +++ b/homeassistant/components/deluge/switch.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import ToggleEntity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['deluge-client==1.4.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Deluge Switch' diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 354f0c0e37541a..50d1eebdcd383d 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -7,7 +7,6 @@ import homeassistant.core as ha from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM -DEPENDENCIES = ['conversation', 'zone'] DOMAIN = 'demo' COMPONENTS_WITH_DEMO_PLATFORM = [ diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index 0adafe4f472e2e..da416ce8045bc6 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -18,8 +18,6 @@ STATE_PAUSED, STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['denonavr==0.7.8'] - _LOGGER = logging.getLogger(__name__) ATTR_SOUND_MODE_RAW = 'sound_mode_raw' diff --git a/homeassistant/components/deutsche_bahn/sensor.py b/homeassistant/components/deutsche_bahn/sensor.py index 41584b2561fb9d..9c7518eb8ef7a6 100644 --- a/homeassistant/components/deutsche_bahn/sensor.py +++ b/homeassistant/components/deutsche_bahn/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.util.dt as dt_util -REQUIREMENTS = ['schiene==0.23'] - _LOGGER = logging.getLogger(__name__) CONF_DESTINATION = 'to' diff --git a/homeassistant/components/device_sun_light_trigger/__init__.py b/homeassistant/components/device_sun_light_trigger/__init__.py index 00adefc6b5c31f..945f83686712b5 100644 --- a/homeassistant/components/device_sun_light_trigger/__init__.py +++ b/homeassistant/components/device_sun_light_trigger/__init__.py @@ -17,8 +17,6 @@ import homeassistant.helpers.config_validation as cv DOMAIN = 'device_sun_light_trigger' -DEPENDENCIES = ['light', 'device_tracker', 'group'] - CONF_DEVICE_GROUP = 'device_group' CONF_DISABLE_TURN_OFF = 'disable_turn_off' CONF_LIGHT_GROUP = 'light_group' diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 42d301721dad47..60dac103a46f14 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -35,8 +35,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'device_tracker' -DEPENDENCIES = ['zone', 'group'] - GROUP_NAME_ALL_DEVICES = 'all devices' ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format('all_devices') diff --git a/homeassistant/components/dht/sensor.py b/homeassistant/components/dht/sensor.py index 719c2525f0a985..d544bfa74e85aa 100644 --- a/homeassistant/components/dht/sensor.py +++ b/homeassistant/components/dht/sensor.py @@ -12,8 +12,6 @@ from homeassistant.util import Throttle from homeassistant.util.temperature import celsius_to_fahrenheit -REQUIREMENTS = ['Adafruit-DHT==1.4.0'] - _LOGGER = logging.getLogger(__name__) CONF_PIN = 'pin' diff --git a/homeassistant/components/dialogflow/__init__.py b/homeassistant/components/dialogflow/__init__.py index 1536fe3d2362ca..a6134d4b19c04d 100644 --- a/homeassistant/components/dialogflow/__init__.py +++ b/homeassistant/components/dialogflow/__init__.py @@ -10,7 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['webhook'] DOMAIN = 'dialogflow' SOURCE = "Home Assistant Dialogflow" diff --git a/homeassistant/components/digital_ocean/__init__.py b/homeassistant/components/digital_ocean/__init__.py index 7975a6eea0d69c..9e034b2428dda2 100644 --- a/homeassistant/components/digital_ocean/__init__.py +++ b/homeassistant/components/digital_ocean/__init__.py @@ -8,8 +8,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-digitalocean==1.13.2'] - _LOGGER = logging.getLogger(__name__) ATTR_CREATED_AT = 'created_at' diff --git a/homeassistant/components/digital_ocean/binary_sensor.py b/homeassistant/components/digital_ocean/binary_sensor.py index d496a09161b91d..83406247a07e9a 100644 --- a/homeassistant/components/digital_ocean/binary_sensor.py +++ b/homeassistant/components/digital_ocean/binary_sensor.py @@ -17,8 +17,6 @@ DEFAULT_NAME = 'Droplet' DEFAULT_DEVICE_CLASS = 'moving' -DEPENDENCIES = ['digital_ocean'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_DROPLETS): vol.All(cv.ensure_list, [cv.string]), }) diff --git a/homeassistant/components/digital_ocean/switch.py b/homeassistant/components/digital_ocean/switch.py index bc4a6a29b42064..8016ccef0ea86d 100644 --- a/homeassistant/components/digital_ocean/switch.py +++ b/homeassistant/components/digital_ocean/switch.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['digital_ocean'] - DEFAULT_NAME = 'Droplet' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/digitalloggers/switch.py b/homeassistant/components/digitalloggers/switch.py index 89973cfad0c828..4d1a87c44f90f1 100644 --- a/homeassistant/components/digitalloggers/switch.py +++ b/homeassistant/components/digitalloggers/switch.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['dlipower==0.7.165'] - _LOGGER = logging.getLogger(__name__) CONF_CYCLETIME = 'cycletime' diff --git a/homeassistant/components/directv/media_player.py b/homeassistant/components/directv/media_player.py index 3a30282bdf49be..aaffd44d572413 100644 --- a/homeassistant/components/directv/media_player.py +++ b/homeassistant/components/directv/media_player.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['directpy==0.5'] - _LOGGER = logging.getLogger(__name__) ATTR_MEDIA_CURRENTLY_RECORDING = 'media_currently_recording' diff --git a/homeassistant/components/discogs/sensor.py b/homeassistant/components/discogs/sensor.py index f8d66688b4ff65..f9f821668f9d5d 100644 --- a/homeassistant/components/discogs/sensor.py +++ b/homeassistant/components/discogs/sensor.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['discogs_client==2.2.1'] - _LOGGER = logging.getLogger(__name__) ATTR_IDENTITY = 'identity' diff --git a/homeassistant/components/discord/notify.py b/homeassistant/components/discord/notify.py index cb6fc8329c66b5..faf79d14e3325b 100644 --- a/homeassistant/components/discord/notify.py +++ b/homeassistant/components/discord/notify.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['discord.py==0.16.12'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_TOKEN): cv.string }) diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 8e3a350c5ca1b0..7490b53092642b 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -20,8 +20,6 @@ from homeassistant.helpers.discovery import async_load_platform, async_discover import homeassistant.util.dt as dt_util -REQUIREMENTS = ['netdisco==2.6.0'] - DOMAIN = 'discovery' SCAN_INTERVAL = timedelta(seconds=300) diff --git a/homeassistant/components/dlib_face_detect/image_processing.py b/homeassistant/components/dlib_face_detect/image_processing.py index 49fbfadff7e779..0bc657a615d7ff 100644 --- a/homeassistant/components/dlib_face_detect/image_processing.py +++ b/homeassistant/components/dlib_face_detect/image_processing.py @@ -8,8 +8,6 @@ from homeassistant.components.image_processing import ( ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) -REQUIREMENTS = ['face_recognition==1.2.3'] - _LOGGER = logging.getLogger(__name__) ATTR_LOCATION = 'location' diff --git a/homeassistant/components/dlib_face_identify/image_processing.py b/homeassistant/components/dlib_face_identify/image_processing.py index a3b91235125e43..569b1ecece2bcf 100644 --- a/homeassistant/components/dlib_face_identify/image_processing.py +++ b/homeassistant/components/dlib_face_identify/image_processing.py @@ -10,8 +10,6 @@ CONF_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['face_recognition==1.2.3'] - _LOGGER = logging.getLogger(__name__) ATTR_NAME = 'name' diff --git a/homeassistant/components/dlink/switch.py b/homeassistant/components/dlink/switch.py index 812fd3882b311b..7164bb2310a914 100644 --- a/homeassistant/components/dlink/switch.py +++ b/homeassistant/components/dlink/switch.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import dt as dt_util -REQUIREMENTS = ['pyW215==0.6.0'] - _LOGGER = logging.getLogger(__name__) ATTR_TOTAL_CONSUMPTION = 'total_consumption' diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 54c19f70ef324d..6f29bd65d56772 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -27,8 +27,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import get_local_ip -REQUIREMENTS = ['async-upnp-client==0.14.7'] - _LOGGER = logging.getLogger(__name__) DLNA_DMR_DATA = 'dlna_dmr' diff --git a/homeassistant/components/dnsip/sensor.py b/homeassistant/components/dnsip/sensor.py index 13c9be7bb14fb5..976abb1401b57f 100644 --- a/homeassistant/components/dnsip/sensor.py +++ b/homeassistant/components/dnsip/sensor.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['aiodns==1.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_NAME = 'name' diff --git a/homeassistant/components/dominos/__init__.py b/homeassistant/components/dominos/__init__.py index 1c8966f3b4b5d9..3c5cb3ed6ecc56 100644 --- a/homeassistant/components/dominos/__init__.py +++ b/homeassistant/components/dominos/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.util import Throttle -REQUIREMENTS = ['pizzapi==0.0.3'] - _LOGGER = logging.getLogger(__name__) # The domain of your component. Should be equal to the name of your component. @@ -34,8 +32,6 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10) MIN_TIME_BETWEEN_STORE_UPDATES = timedelta(minutes=3330) -DEPENDENCIES = ['http'] - _ORDERS_SCHEMA = vol.Schema({ vol.Required(ATTR_ORDER_NAME): cv.string, vol.Required(ATTR_ORDER_CODES): vol.All(cv.ensure_list, [cv.string]), diff --git a/homeassistant/components/doorbird/__init__.py b/homeassistant/components/doorbird/__init__.py index 25a2c5caff91d9..477d96770bc523 100644 --- a/homeassistant/components/doorbird/__init__.py +++ b/homeassistant/components/doorbird/__init__.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import dt as dt_util, slugify -REQUIREMENTS = ['doorbirdpy==2.0.8'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'doorbird' diff --git a/homeassistant/components/doorbird/camera.py b/homeassistant/components/doorbird/camera.py index a93b0fbf1948cb..9a20a91c75855d 100644 --- a/homeassistant/components/doorbird/camera.py +++ b/homeassistant/components/doorbird/camera.py @@ -11,8 +11,6 @@ from . import DOMAIN as DOORBIRD_DOMAIN -DEPENDENCIES = ['doorbird'] - _CAMERA_LAST_VISITOR = "{} Last Ring" _CAMERA_LAST_MOTION = "{} Last Motion" _CAMERA_LIVE = "{} Live" diff --git a/homeassistant/components/doorbird/switch.py b/homeassistant/components/doorbird/switch.py index ba6f96660d1b2d..f3b1f5f059e653 100644 --- a/homeassistant/components/doorbird/switch.py +++ b/homeassistant/components/doorbird/switch.py @@ -6,8 +6,6 @@ from . import DOMAIN as DOORBIRD_DOMAIN -DEPENDENCIES = ['doorbird'] - _LOGGER = logging.getLogger(__name__) IR_RELAY = '__ir_light__' diff --git a/homeassistant/components/dovado/__init__.py b/homeassistant/components/dovado/__init__.py index df2eed3011a873..2a240c2a79ec79 100644 --- a/homeassistant/components/dovado/__init__.py +++ b/homeassistant/components/dovado/__init__.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['dovado==0.4.1'] - DOMAIN = 'dovado' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/dovado/notify.py b/homeassistant/components/dovado/notify.py index 59827529ed3ad5..f9d9e5574a103c 100644 --- a/homeassistant/components/dovado/notify.py +++ b/homeassistant/components/dovado/notify.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['dovado'] - def get_service(hass, config, discovery_info=None): """Get the Dovado Router SMS notification service.""" diff --git a/homeassistant/components/dovado/sensor.py b/homeassistant/components/dovado/sensor.py index 56c4ee03a3ada9..7a825118fc6b54 100644 --- a/homeassistant/components/dovado/sensor.py +++ b/homeassistant/components/dovado/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['dovado'] - MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) SENSOR_UPLOAD = 'upload' diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 74f6cb37fc2ebc..d7acc5c28bf897 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['dsmr_parser==0.12'] - CONF_DSMR_VERSION = 'dsmr_version' CONF_RECONNECT_INTERVAL = 'reconnect_interval' CONF_PRECISION = 'precision' diff --git a/homeassistant/components/duke_energy/sensor.py b/homeassistant/components/duke_energy/sensor.py index 9aada34841880a..e364e35048b6ee 100644 --- a/homeassistant/components/duke_energy/sensor.py +++ b/homeassistant/components/duke_energy/sensor.py @@ -8,8 +8,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pydukeenergy==0.0.6'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/dunehd/media_player.py b/homeassistant/components/dunehd/media_player.py index 70d96424ced97e..a5698c74654382 100644 --- a/homeassistant/components/dunehd/media_player.py +++ b/homeassistant/components/dunehd/media_player.py @@ -11,8 +11,6 @@ CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pdunehd==1.3'] - DEFAULT_NAME = 'DuneHD' CONF_SOURCES = 'sources' diff --git a/homeassistant/components/dweet/__init__.py b/homeassistant/components/dweet/__init__.py index f8e5b1811632e4..148eeeec9a42df 100644 --- a/homeassistant/components/dweet/__init__.py +++ b/homeassistant/components/dweet/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers import state as state_helper from homeassistant.util import Throttle -REQUIREMENTS = ['dweepy==0.3.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'dweet' diff --git a/homeassistant/components/dweet/sensor.py b/homeassistant/components/dweet/sensor.py index d1a64201e6dc48..55f3c5341a3301 100644 --- a/homeassistant/components/dweet/sensor.py +++ b/homeassistant/components/dweet/sensor.py @@ -11,8 +11,6 @@ CONF_NAME, CONF_VALUE_TEMPLATE, CONF_UNIT_OF_MEASUREMENT, CONF_DEVICE) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['dweepy==0.3.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Dweet.io Sensor' diff --git a/homeassistant/components/dyson/__init__.py b/homeassistant/components/dyson/__init__.py index eccf8aac364c04..a857d6657fd422 100644 --- a/homeassistant/components/dyson/__init__.py +++ b/homeassistant/components/dyson/__init__.py @@ -8,8 +8,6 @@ CONF_DEVICES, CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME) from homeassistant.helpers import discovery -REQUIREMENTS = ['libpurecool==0.5.0'] - _LOGGER = logging.getLogger(__name__) CONF_LANGUAGE = 'language' diff --git a/homeassistant/components/dyson/fan.py b/homeassistant/components/dyson/fan.py index 0140378968b60b..03a55f8abbea80 100644 --- a/homeassistant/components/dyson/fan.py +++ b/homeassistant/components/dyson/fan.py @@ -27,7 +27,6 @@ ATTR_DYSON_SPEED = 'dyson_speed' ATTR_DYSON_SPEED_LIST = 'dyson_speed_list' -DEPENDENCIES = ['dyson'] DYSON_DOMAIN = 'dyson' DYSON_FAN_DEVICES = 'dyson_fan_devices' diff --git a/homeassistant/components/dyson/sensor.py b/homeassistant/components/dyson/sensor.py index 2c7a71f5724847..56c924d1a54e75 100644 --- a/homeassistant/components/dyson/sensor.py +++ b/homeassistant/components/dyson/sensor.py @@ -6,8 +6,6 @@ from . import DYSON_DEVICES -DEPENDENCIES = ['dyson'] - SENSOR_UNITS = { 'air_quality': None, 'dust': None, diff --git a/homeassistant/components/dyson/vacuum.py b/homeassistant/components/dyson/vacuum.py index f1822b4043b461..0bb2368f690372 100644 --- a/homeassistant/components/dyson/vacuum.py +++ b/homeassistant/components/dyson/vacuum.py @@ -15,8 +15,6 @@ ATTR_FULL_CLEAN_TYPE = 'full_clean_type' ATTR_POSITION = 'position' -DEPENDENCIES = ['dyson'] - DYSON_360_EYE_DEVICES = "dyson_360_eye_devices" SUPPORT_DYSON = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PAUSE | \ diff --git a/homeassistant/components/ebox/sensor.py b/homeassistant/components/ebox/sensor.py index 24458e444dc7a9..aaf3384d55ff38 100644 --- a/homeassistant/components/ebox/sensor.py +++ b/homeassistant/components/ebox/sensor.py @@ -21,8 +21,6 @@ from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['pyebox==1.1.4'] - _LOGGER = logging.getLogger(__name__) GIGABITS = 'Gb' # type: str diff --git a/homeassistant/components/ebusd/__init__.py b/homeassistant/components/ebusd/__init__.py index bc1b3aa9595f9b..15ff523f4fbf90 100644 --- a/homeassistant/components/ebusd/__init__.py +++ b/homeassistant/components/ebusd/__init__.py @@ -13,8 +13,6 @@ from .const import (DOMAIN, SENSOR_TYPES) -REQUIREMENTS = ['ebusdpy==0.0.16'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'ebusd' diff --git a/homeassistant/components/ebusd/sensor.py b/homeassistant/components/ebusd/sensor.py index 942ba107509a94..f73bb09b509696 100644 --- a/homeassistant/components/ebusd/sensor.py +++ b/homeassistant/components/ebusd/sensor.py @@ -6,8 +6,6 @@ from .const import DOMAIN -DEPENDENCIES = ['ebusd'] - TIME_FRAME1_BEGIN = 'time_frame1_begin' TIME_FRAME1_END = 'time_frame1_end' TIME_FRAME2_BEGIN = 'time_frame2_begin' diff --git a/homeassistant/components/ecoal_boiler/__init__.py b/homeassistant/components/ecoal_boiler/__init__.py index 6ab9fc3181cb9c..796324d9337dbe 100644 --- a/homeassistant/components/ecoal_boiler/__init__.py +++ b/homeassistant/components/ecoal_boiler/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform -REQUIREMENTS = ['ecoaliface==0.4.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "ecoal_boiler" diff --git a/homeassistant/components/ecoal_boiler/sensor.py b/homeassistant/components/ecoal_boiler/sensor.py index ef8b39842d9a0f..f1998dd5b2e3d0 100644 --- a/homeassistant/components/ecoal_boiler/sensor.py +++ b/homeassistant/components/ecoal_boiler/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ecoal_boiler'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ecoal sensors.""" diff --git a/homeassistant/components/ecoal_boiler/switch.py b/homeassistant/components/ecoal_boiler/switch.py index db8759a032acc9..9f286e625a5206 100644 --- a/homeassistant/components/ecoal_boiler/switch.py +++ b/homeassistant/components/ecoal_boiler/switch.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ecoal_boiler'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up switches based on ecoal interface.""" diff --git a/homeassistant/components/ecobee/__init__.py b/homeassistant/components/ecobee/__init__.py index 167132a5f41f16..5f9ae6a919da14 100644 --- a/homeassistant/components/ecobee/__init__.py +++ b/homeassistant/components/ecobee/__init__.py @@ -11,8 +11,6 @@ from homeassistant.util import Throttle from homeassistant.util.json import save_json -REQUIREMENTS = ['python-ecobee-api==0.0.18'] - _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ecobee/binary_sensor.py b/homeassistant/components/ecobee/binary_sensor.py index ca8e551bf5e6f2..0989b9ded976c9 100644 --- a/homeassistant/components/ecobee/binary_sensor.py +++ b/homeassistant/components/ecobee/binary_sensor.py @@ -2,8 +2,6 @@ from homeassistant.components import ecobee from homeassistant.components.binary_sensor import BinarySensorDevice -DEPENDENCIES = ['ecobee'] - ECOBEE_CONFIG_FILE = 'ecobee.conf' diff --git a/homeassistant/components/ecobee/climate.py b/homeassistant/components/ecobee/climate.py index 44a3800afa958f..3fe1646ee02b75 100644 --- a/homeassistant/components/ecobee/climate.py +++ b/homeassistant/components/ecobee/climate.py @@ -27,8 +27,6 @@ VACATION_HOLD = 'vacation' AWAY_MODE = 'awayMode' -DEPENDENCIES = ['ecobee'] - SERVICE_SET_FAN_MIN_ON_TIME = 'ecobee_set_fan_min_on_time' SERVICE_RESUME_PROGRAM = 'ecobee_resume_program' diff --git a/homeassistant/components/ecobee/notify.py b/homeassistant/components/ecobee/notify.py index 9824d20b85e98e..d6e4e8f0c6320c 100644 --- a/homeassistant/components/ecobee/notify.py +++ b/homeassistant/components/ecobee/notify.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ecobee'] - CONF_INDEX = 'index' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/ecobee/sensor.py b/homeassistant/components/ecobee/sensor.py index 1f9fd5cbde8548..436903a645f35c 100644 --- a/homeassistant/components/ecobee/sensor.py +++ b/homeassistant/components/ecobee/sensor.py @@ -4,8 +4,6 @@ DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import Entity -DEPENDENCIES = ['ecobee'] - ECOBEE_CONFIG_FILE = 'ecobee.conf' SENSOR_TYPES = { diff --git a/homeassistant/components/ecobee/weather.py b/homeassistant/components/ecobee/weather.py index 2ba5f362b7d829..f5058434f387bb 100644 --- a/homeassistant/components/ecobee/weather.py +++ b/homeassistant/components/ecobee/weather.py @@ -7,8 +7,6 @@ ATTR_FORECAST_TIME, ATTR_FORECAST_WIND_SPEED, WeatherEntity) from homeassistant.const import TEMP_FAHRENHEIT -DEPENDENCIES = ['ecobee'] - ATTR_FORECAST_TEMP_HIGH = 'temphigh' ATTR_FORECAST_PRESSURE = 'pressure' ATTR_FORECAST_VISIBILITY = 'visibility' diff --git a/homeassistant/components/econet/water_heater.py b/homeassistant/components/econet/water_heater.py index 90176842bf1175..4c47e24d705bbf 100644 --- a/homeassistant/components/econet/water_heater.py +++ b/homeassistant/components/econet/water_heater.py @@ -13,8 +13,6 @@ TEMP_FAHRENHEIT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyeconet==0.0.10'] - _LOGGER = logging.getLogger(__name__) ATTR_VACATION_START = 'next_vacation_start_date' diff --git a/homeassistant/components/ecovacs/__init__.py b/homeassistant/components/ecovacs/__init__.py index 124cae3ca47191..da87af722a60a8 100644 --- a/homeassistant/components/ecovacs/__init__.py +++ b/homeassistant/components/ecovacs/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['sucks==0.9.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "ecovacs" diff --git a/homeassistant/components/ecovacs/vacuum.py b/homeassistant/components/ecovacs/vacuum.py index b9fe94f2bed0cb..ee374871d31804 100644 --- a/homeassistant/components/ecovacs/vacuum.py +++ b/homeassistant/components/ecovacs/vacuum.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ecovacs'] - SUPPORT_ECOVACS = ( SUPPORT_BATTERY | SUPPORT_RETURN_HOME | SUPPORT_CLEAN_SPOT | SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON | SUPPORT_LOCATE | diff --git a/homeassistant/components/eddystone_temperature/sensor.py b/homeassistant/components/eddystone_temperature/sensor.py index ae3d498d30c0d9..aad279934e585f 100644 --- a/homeassistant/components/eddystone_temperature/sensor.py +++ b/homeassistant/components/eddystone_temperature/sensor.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['beacontools[scan]==1.2.3', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) CONF_BEACONS = 'beacons' diff --git a/homeassistant/components/edimax/switch.py b/homeassistant/components/edimax/switch.py index 338e6ac932cc4b..535ae65800fb1a 100644 --- a/homeassistant/components/edimax/switch.py +++ b/homeassistant/components/edimax/switch.py @@ -8,8 +8,6 @@ CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyedimax==0.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Edimax Smart Plug' diff --git a/homeassistant/components/edp_redy/__init__.py b/homeassistant/components/edp_redy/__init__.py index 9b8bfaa437a12d..af01206419468a 100644 --- a/homeassistant/components/edp_redy/__init__.py +++ b/homeassistant/components/edp_redy/__init__.py @@ -20,8 +20,6 @@ DATA_UPDATE_TOPIC = '{0}_data_update'.format(DOMAIN) UPDATE_INTERVAL = 60 -REQUIREMENTS = ['edp_redy==0.0.3'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_USERNAME): cv.string, diff --git a/homeassistant/components/edp_redy/sensor.py b/homeassistant/components/edp_redy/sensor.py index b8f9c031c298c9..cf9766ede66d03 100644 --- a/homeassistant/components/edp_redy/sensor.py +++ b/homeassistant/components/edp_redy/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['edp_redy'] - # Load power in watts (W) ATTR_ACTIVE_POWER = 'active_power' diff --git a/homeassistant/components/edp_redy/switch.py b/homeassistant/components/edp_redy/switch.py index 0c92f80ccf6366..3f6dfe6b82d498 100644 --- a/homeassistant/components/edp_redy/switch.py +++ b/homeassistant/components/edp_redy/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['edp_redy'] - # Load power in watts (W) ATTR_ACTIVE_POWER = 'active_power' diff --git a/homeassistant/components/ee_brightbox/device_tracker.py b/homeassistant/components/ee_brightbox/device_tracker.py index 46e4a3c3c24307..6af5065ed2e691 100644 --- a/homeassistant/components/ee_brightbox/device_tracker.py +++ b/homeassistant/components/ee_brightbox/device_tracker.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['eebrightbox==0.0.4'] - _LOGGER = logging.getLogger(__name__) CONF_VERSION = 'version' diff --git a/homeassistant/components/egardia/__init__.py b/homeassistant/components/egardia/__init__.py index fe613824c9512d..cf0bb20f0fcec5 100644 --- a/homeassistant/components/egardia/__init__.py +++ b/homeassistant/components/egardia/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pythonegardia==1.0.39'] - _LOGGER = logging.getLogger(__name__) ATTR_DISCOVER_DEVICES = 'egardia_sensor' diff --git a/homeassistant/components/egardia/alarm_control_panel.py b/homeassistant/components/egardia/alarm_control_panel.py index 7fc60d5fb5db6e..ab48181f9ede14 100644 --- a/homeassistant/components/egardia/alarm_control_panel.py +++ b/homeassistant/components/egardia/alarm_control_panel.py @@ -13,8 +13,6 @@ CONF_REPORT_SERVER_PORT, EGARDIA_DEVICE, EGARDIA_SERVER, REPORT_SERVER_CODES_IGNORE) -DEPENDENCIES = ['egardia'] - _LOGGER = logging.getLogger(__name__) STATES = { diff --git a/homeassistant/components/egardia/binary_sensor.py b/homeassistant/components/egardia/binary_sensor.py index d11894ae675666..965b2dd1d5509e 100644 --- a/homeassistant/components/egardia/binary_sensor.py +++ b/homeassistant/components/egardia/binary_sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['egardia'] - EGARDIA_TYPE_TO_DEVICE_CLASS = { 'IR Sensor': 'motion', 'Door Contact': 'opening', diff --git a/homeassistant/components/eight_sleep/__init__.py b/homeassistant/components/eight_sleep/__init__.py index ca6c8a5a5c6070..d74218796a3168 100644 --- a/homeassistant/components/eight_sleep/__init__.py +++ b/homeassistant/components/eight_sleep/__init__.py @@ -16,8 +16,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -REQUIREMENTS = ['pyeight==0.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_PARTNER = 'partner' diff --git a/homeassistant/components/eight_sleep/binary_sensor.py b/homeassistant/components/eight_sleep/binary_sensor.py index a3ca27b570de0c..b3842106723063 100644 --- a/homeassistant/components/eight_sleep/binary_sensor.py +++ b/homeassistant/components/eight_sleep/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['eight_sleep'] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index a1ad93ec54a3f3..b7b0f5881552bf 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -5,8 +5,6 @@ CONF_SENSORS, DATA_EIGHT, NAME_MAP, EightSleepHeatEntity, EightSleepUserEntity) -DEPENDENCIES = ['eight_sleep'] - ATTR_ROOM_TEMP = 'Room Temperature' ATTR_AVG_ROOM_TEMP = 'Average Room Temperature' ATTR_BED_TEMP = 'Bed Temperature' diff --git a/homeassistant/components/eliqonline/sensor.py b/homeassistant/components/eliqonline/sensor.py index 198ca327997808..12752b8db9e001 100644 --- a/homeassistant/components/eliqonline/sensor.py +++ b/homeassistant/components/eliqonline/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['eliqonline==1.2.2'] - _LOGGER = logging.getLogger(__name__) CONF_CHANNEL_ID = 'channel_id' diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index a0c08bf54299f5..564f0e74c750fc 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ConfigType # noqa -REQUIREMENTS = ['elkm1-lib==0.7.13'] - DOMAIN = 'elkm1' CONF_AREA = 'area' diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index e9155dd17b5bf7..b885913a0df047 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -12,8 +12,6 @@ from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - SIGNAL_ARM_ENTITY = 'elkm1_arm' SIGNAL_DISPLAY_MESSAGE = 'elkm1_display_message' diff --git a/homeassistant/components/elkm1/climate.py b/homeassistant/components/elkm1/climate.py index 93e4aa66b23494..23c1831286310c 100644 --- a/homeassistant/components/elkm1/climate.py +++ b/homeassistant/components/elkm1/climate.py @@ -9,8 +9,6 @@ from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/elkm1/light.py b/homeassistant/components/elkm1/light.py index fe84ab3f251895..ee6fe09a7a23fc 100644 --- a/homeassistant/components/elkm1/light.py +++ b/homeassistant/components/elkm1/light.py @@ -4,8 +4,6 @@ from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/elkm1/scene.py b/homeassistant/components/elkm1/scene.py index 1d08f4cf96d5f1..aaae8bb0a5cf45 100644 --- a/homeassistant/components/elkm1/scene.py +++ b/homeassistant/components/elkm1/scene.py @@ -3,8 +3,6 @@ from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/elkm1/sensor.py b/homeassistant/components/elkm1/sensor.py index da27a3ac4b105d..0e367265605108 100644 --- a/homeassistant/components/elkm1/sensor.py +++ b/homeassistant/components/elkm1/sensor.py @@ -1,8 +1,6 @@ """Support for control of ElkM1 sensors.""" from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/elkm1/switch.py b/homeassistant/components/elkm1/switch.py index 740a296586502f..df29491435e2c4 100644 --- a/homeassistant/components/elkm1/switch.py +++ b/homeassistant/components/elkm1/switch.py @@ -3,8 +3,6 @@ from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities -DEPENDENCIES = [ELK_DOMAIN] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/emby/media_player.py b/homeassistant/components/emby/media_player.py index 8a94664f352cbe..fa1c096707be4b 100644 --- a/homeassistant/components/emby/media_player.py +++ b/homeassistant/components/emby/media_player.py @@ -17,8 +17,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyemby==1.6'] - _LOGGER = logging.getLogger(__name__) CONF_AUTO_HIDE = 'auto_hide' diff --git a/homeassistant/components/emulated_hue/__init__.py b/homeassistant/components/emulated_hue/__init__.py index c8ed263a2dc208..2ef0aaca134523 100644 --- a/homeassistant/components/emulated_hue/__init__.py +++ b/homeassistant/components/emulated_hue/__init__.py @@ -8,7 +8,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ) -from homeassistant.components.http import REQUIREMENTS # NOQA from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.deprecation import get_deprecated import homeassistant.helpers.config_validation as cv diff --git a/homeassistant/components/emulated_roku/__init__.py b/homeassistant/components/emulated_roku/__init__.py index ef87e14ec434bf..72d4dff72db1f0 100644 --- a/homeassistant/components/emulated_roku/__init__.py +++ b/homeassistant/components/emulated_roku/__init__.py @@ -11,8 +11,6 @@ CONF_ADVERTISE_IP, CONF_ADVERTISE_PORT, CONF_HOST_IP, CONF_LISTEN_PORT, CONF_SERVERS, CONF_UPNP_BIND_MULTICAST, DOMAIN) -REQUIREMENTS = ['emulated_roku==0.1.8'] - SERVER_CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_LISTEN_PORT): cv.port, diff --git a/homeassistant/components/enigma2/media_player.py b/homeassistant/components/enigma2/media_player.py index 11c3e0fe3ceab4..4662c7076376d0 100644 --- a/homeassistant/components/enigma2/media_player.py +++ b/homeassistant/components/enigma2/media_player.py @@ -14,8 +14,6 @@ STATE_OFF, STATE_ON, STATE_PLAYING, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['openwebifpy==3.1.1'] - _LOGGER = logging.getLogger(__name__) ATTR_MEDIA_CURRENTLY_RECORDING = 'media_currently_recording' diff --git a/homeassistant/components/enocean/__init__.py b/homeassistant/components/enocean/__init__.py index 8b3c27025cd26e..2dcf6a3a0ac514 100644 --- a/homeassistant/components/enocean/__init__.py +++ b/homeassistant/components/enocean/__init__.py @@ -6,8 +6,6 @@ from homeassistant.const import CONF_DEVICE import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['enocean==0.40'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'enocean' diff --git a/homeassistant/components/enocean/binary_sensor.py b/homeassistant/components/enocean/binary_sensor.py index 1fde8c79e401dd..649bec024e3db3 100644 --- a/homeassistant/components/enocean/binary_sensor.py +++ b/homeassistant/components/enocean/binary_sensor.py @@ -12,7 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['enocean'] DEFAULT_NAME = 'EnOcean binary sensor' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/enocean/light.py b/homeassistant/components/enocean/light.py index f574f89f951f77..9ec3f4ab27bd33 100644 --- a/homeassistant/components/enocean/light.py +++ b/homeassistant/components/enocean/light.py @@ -15,8 +15,6 @@ CONF_SENDER_ID = 'sender_id' DEFAULT_NAME = 'EnOcean Light' -DEPENDENCIES = ['enocean'] - SUPPORT_ENOCEAN = SUPPORT_BRIGHTNESS PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/enocean/sensor.py b/homeassistant/components/enocean/sensor.py index 8d79de2c50d700..530738e1f88a7c 100644 --- a/homeassistant/components/enocean/sensor.py +++ b/homeassistant/components/enocean/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'EnOcean sensor' -DEPENDENCIES = ['enocean'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/enocean/switch.py b/homeassistant/components/enocean/switch.py index 4dfbafd36b16f6..f0b132c9d1c2a8 100644 --- a/homeassistant/components/enocean/switch.py +++ b/homeassistant/components/enocean/switch.py @@ -12,7 +12,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'EnOcean Switch' -DEPENDENCIES = ['enocean'] CONF_CHANNEL = 'channel' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/enphase_envoy/sensor.py b/homeassistant/components/enphase_envoy/sensor.py index 2b62732dc9106f..7077e12d7500a1 100644 --- a/homeassistant/components/enphase_envoy/sensor.py +++ b/homeassistant/components/enphase_envoy/sensor.py @@ -10,7 +10,6 @@ CONF_IP_ADDRESS, CONF_MONITORED_CONDITIONS, POWER_WATT) -REQUIREMENTS = ['envoy_reader==0.3'] _LOGGER = logging.getLogger(__name__) SENSORS = { diff --git a/homeassistant/components/entur_public_transport/sensor.py b/homeassistant/components/entur_public_transport/sensor.py index b2e228676902f3..61b183b9408da9 100644 --- a/homeassistant/components/entur_public_transport/sensor.py +++ b/homeassistant/components/entur_public_transport/sensor.py @@ -14,8 +14,6 @@ from homeassistant.util import Throttle import homeassistant.util.dt as dt_util -REQUIREMENTS = ['enturclient==0.2.0'] - _LOGGER = logging.getLogger(__name__) API_CLIENT_NAME = 'homeassistant-homeassistant' diff --git a/homeassistant/components/envirophat/sensor.py b/homeassistant/components/envirophat/sensor.py index 16cb79406a9b9d..6d792df24217c5 100644 --- a/homeassistant/components/envirophat/sensor.py +++ b/homeassistant/components/envirophat/sensor.py @@ -11,9 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['envirophat==0.0.6', - 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'envirophat' diff --git a/homeassistant/components/envisalink/__init__.py b/homeassistant/components/envisalink/__init__.py index c46a26c6f857f3..d7a015e8e45710 100644 --- a/homeassistant/components/envisalink/__init__.py +++ b/homeassistant/components/envisalink/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['pyenvisalink==3.8'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'envisalink' diff --git a/homeassistant/components/envisalink/alarm_control_panel.py b/homeassistant/components/envisalink/alarm_control_panel.py index 44874c6d5e8445..91a59d8f842c56 100644 --- a/homeassistant/components/envisalink/alarm_control_panel.py +++ b/homeassistant/components/envisalink/alarm_control_panel.py @@ -18,8 +18,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['envisalink'] - SERVICE_ALARM_KEYPRESS = 'envisalink_alarm_keypress' ATTR_KEYPRESS = 'keypress' ALARM_KEYPRESS_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/envisalink/binary_sensor.py b/homeassistant/components/envisalink/binary_sensor.py index 267bba8cd288ee..bf47749d22862f 100644 --- a/homeassistant/components/envisalink/binary_sensor.py +++ b/homeassistant/components/envisalink/binary_sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['envisalink'] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/envisalink/sensor.py b/homeassistant/components/envisalink/sensor.py index 67a601b02a2ff5..2652a7e2137fb2 100644 --- a/homeassistant/components/envisalink/sensor.py +++ b/homeassistant/components/envisalink/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['envisalink'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/ephember/climate.py b/homeassistant/components/ephember/climate.py index 3052dd911ee5de..4e741dacf9d752 100644 --- a/homeassistant/components/ephember/climate.py +++ b/homeassistant/components/ephember/climate.py @@ -11,8 +11,6 @@ ATTR_TEMPERATURE, TEMP_CELSIUS, CONF_USERNAME, CONF_PASSWORD, STATE_OFF) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyephember==0.2.0'] - _LOGGER = logging.getLogger(__name__) # Return cached results if last scan was less then this time ago diff --git a/homeassistant/components/epson/media_player.py b/homeassistant/components/epson/media_player.py index 57bd18e0ee0e50..8273ca9a21a1e2 100644 --- a/homeassistant/components/epson/media_player.py +++ b/homeassistant/components/epson/media_player.py @@ -15,8 +15,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['epson-projector==0.1.3'] - _LOGGER = logging.getLogger(__name__) ATTR_CMODE = 'cmode' diff --git a/homeassistant/components/eq3btsmart/climate.py b/homeassistant/components/eq3btsmart/climate.py index f02bd2bc9a55e2..fc12438fcf37d0 100644 --- a/homeassistant/components/eq3btsmart/climate.py +++ b/homeassistant/components/eq3btsmart/climate.py @@ -13,8 +13,6 @@ TEMP_CELSIUS, PRECISION_HALVES) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-eq3bt==0.1.9', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) STATE_BOOST = 'boost' diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 9e6f6367cda4fd..9b1e0691d13027 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -32,8 +32,6 @@ ServiceCall, UserService DOMAIN = 'esphome' -REQUIREMENTS = ['aioesphomeapi==2.0.0'] - _LOGGER = logging.getLogger(__name__) DISPATCHER_UPDATE_ENTITY = 'esphome_{entry_id}_update_{component_key}_{key}' diff --git a/homeassistant/components/esphome/binary_sensor.py b/homeassistant/components/esphome/binary_sensor.py index 2db2f209fa5ac9..ff3fc2597925d3 100644 --- a/homeassistant/components/esphome/binary_sensor.py +++ b/homeassistant/components/esphome/binary_sensor.py @@ -10,7 +10,6 @@ # pylint: disable=unused-import from aioesphomeapi import BinarySensorInfo, BinarySensorState # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/camera.py b/homeassistant/components/esphome/camera.py index 319a2c2a4d9150..bb80ca72724374 100644 --- a/homeassistant/components/esphome/camera.py +++ b/homeassistant/components/esphome/camera.py @@ -13,7 +13,6 @@ # pylint: disable=unused-import from aioesphomeapi import CameraInfo, CameraState # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index e3cd9e488bf60d..e95f9e446337f2 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -19,7 +19,6 @@ # pylint: disable=unused-import from aioesphomeapi import ClimateInfo, ClimateState, ClimateMode # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index 68eb4221a931c9..5eb12aa86ec3bf 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -15,7 +15,6 @@ # pylint: disable=unused-import from aioesphomeapi import CoverInfo, CoverState # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py index 973fa85774c0a9..35938de2455023 100644 --- a/homeassistant/components/esphome/fan.py +++ b/homeassistant/components/esphome/fan.py @@ -14,7 +14,6 @@ # pylint: disable=unused-import from aioesphomeapi import FanInfo, FanState, FanSpeed # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/light.py b/homeassistant/components/esphome/light.py index c84c50010d9482..3d55713b123c68 100644 --- a/homeassistant/components/esphome/light.py +++ b/homeassistant/components/esphome/light.py @@ -17,7 +17,6 @@ # pylint: disable=unused-import from aioesphomeapi import LightInfo, LightState # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/sensor.py b/homeassistant/components/esphome/sensor.py index e4fb7ef82baf85..d8ae91e9243228 100644 --- a/homeassistant/components/esphome/sensor.py +++ b/homeassistant/components/esphome/sensor.py @@ -13,7 +13,6 @@ from aioesphomeapi import ( # noqa SensorInfo, SensorState, TextSensorInfo, TextSensorState) -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/esphome/switch.py b/homeassistant/components/esphome/switch.py index e736c1df2097f1..41c5663537c7a7 100644 --- a/homeassistant/components/esphome/switch.py +++ b/homeassistant/components/esphome/switch.py @@ -12,7 +12,6 @@ # pylint: disable=unused-import from aioesphomeapi import SwitchInfo, SwitchState # noqa -DEPENDENCIES = ['esphome'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/etherscan/sensor.py b/homeassistant/components/etherscan/sensor.py index 082295bfea5479..83805ec4d2015b 100644 --- a/homeassistant/components/etherscan/sensor.py +++ b/homeassistant/components/etherscan/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-etherscan-api==0.0.3'] - ATTRIBUTION = "Data provided by etherscan.io" CONF_TOKEN_ADDRESS = 'token_address' diff --git a/homeassistant/components/eufy/__init__.py b/homeassistant/components/eufy/__init__.py index b0bd9109363e39..8425780b76b956 100644 --- a/homeassistant/components/eufy/__init__.py +++ b/homeassistant/components/eufy/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['lakeside==0.12'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'eufy' diff --git a/homeassistant/components/eufy/light.py b/homeassistant/components/eufy/light.py index 62bc058f1555a8..1d08e42fff72fd 100644 --- a/homeassistant/components/eufy/light.py +++ b/homeassistant/components/eufy/light.py @@ -11,8 +11,6 @@ color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) -DEPENDENCIES = ['eufy'] - _LOGGER = logging.getLogger(__name__) EUFY_MAX_KELVIN = 6500 diff --git a/homeassistant/components/eufy/switch.py b/homeassistant/components/eufy/switch.py index 96d68194107217..3216bfed69ea7e 100644 --- a/homeassistant/components/eufy/switch.py +++ b/homeassistant/components/eufy/switch.py @@ -3,8 +3,6 @@ from homeassistant.components.switch import SwitchDevice -DEPENDENCIES = ['eufy'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/everlights/light.py b/homeassistant/components/everlights/light.py index a628f25ea288f0..c5fb025370dfef 100644 --- a/homeassistant/components/everlights/light.py +++ b/homeassistant/components/everlights/light.py @@ -15,8 +15,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['pyeverlights==0.1.0'] - _LOGGER = logging.getLogger(__name__) SUPPORT_EVERLIGHTS = (SUPPORT_EFFECT | SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 87a563ecd6d07f..459a3636a06d3f 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -19,8 +19,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['evohomeclient==0.3.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'evohome' diff --git a/homeassistant/components/familyhub/camera.py b/homeassistant/components/familyhub/camera.py index 18aa969132da79..e9a8bcd94a6c75 100644 --- a/homeassistant/components/familyhub/camera.py +++ b/homeassistant/components/familyhub/camera.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['python-family-hub-local==0.0.2'] - DEFAULT_NAME = 'FamilyHub Camera' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index e67ba390a98e53..23015769f28867 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -18,7 +18,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'fan' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_FANS = 'all fans' diff --git a/homeassistant/components/fastdotcom/__init__.py b/homeassistant/components/fastdotcom/__init__.py index 973cc8e36597f1..3fe860a81fdb1a 100644 --- a/homeassistant/components/fastdotcom/__init__.py +++ b/homeassistant/components/fastdotcom/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['fastdotcom==0.0.3'] - DOMAIN = 'fastdotcom' DATA_UPDATED = '{}_data_updated'.format(DOMAIN) diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py index 37fc0815ddcd93..c9af8e53ab86cb 100644 --- a/homeassistant/components/fastdotcom/sensor.py +++ b/homeassistant/components/fastdotcom/sensor.py @@ -7,8 +7,6 @@ from . import DATA_UPDATED, DOMAIN as FASTDOTCOM_DOMAIN -DEPENDENCIES = ['fastdotcom'] - _LOGGER = logging.getLogger(__name__) ICON = 'mdi:speedometer' diff --git a/homeassistant/components/fedex/sensor.py b/homeassistant/components/fedex/sensor.py index 74ad4f7d0e53c4..aec1cee053c14c 100644 --- a/homeassistant/components/fedex/sensor.py +++ b/homeassistant/components/fedex/sensor.py @@ -14,8 +14,6 @@ from homeassistant.util import slugify from homeassistant.util.dt import now, parse_date -REQUIREMENTS = ['fedexdeliverymanager==1.0.6'] - _LOGGER = logging.getLogger(__name__) COOKIE = 'fedexdeliverymanager_cookies.pickle' diff --git a/homeassistant/components/feedreader/__init__.py b/homeassistant/components/feedreader/__init__.py index 86744bfd39c771..d2acb674ec7d3d 100644 --- a/homeassistant/components/feedreader/__init__.py +++ b/homeassistant/components/feedreader/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.event import track_time_interval import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['feedparser-homeassistant==5.2.2.dev1'] - _LOGGER = getLogger(__name__) CONF_URLS = 'urls' diff --git a/homeassistant/components/ffmpeg/__init__.py b/homeassistant/components/ffmpeg/__init__.py index 05bc1d991678d0..7252e617c5ace7 100644 --- a/homeassistant/components/ffmpeg/__init__.py +++ b/homeassistant/components/ffmpeg/__init__.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['ha-ffmpeg==2.0'] - DOMAIN = 'ffmpeg' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ffmpeg/camera.py b/homeassistant/components/ffmpeg/camera.py index 8bca13cfbb7c94..0e8a69e0bcf3d0 100644 --- a/homeassistant/components/ffmpeg/camera.py +++ b/homeassistant/components/ffmpeg/camera.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ffmpeg'] - DEFAULT_NAME = 'FFmpeg' DEFAULT_ARGUMENTS = "-pred 1" diff --git a/homeassistant/components/ffmpeg_motion/binary_sensor.py b/homeassistant/components/ffmpeg_motion/binary_sensor.py index c274d84329e62d..03aacf3aafbe31 100644 --- a/homeassistant/components/ffmpeg_motion/binary_sensor.py +++ b/homeassistant/components/ffmpeg_motion/binary_sensor.py @@ -12,8 +12,6 @@ CONF_INITIAL_STATE) from homeassistant.const import CONF_NAME -DEPENDENCIES = ['ffmpeg'] - _LOGGER = logging.getLogger(__name__) CONF_RESET = 'reset' diff --git a/homeassistant/components/ffmpeg_noise/binary_sensor.py b/homeassistant/components/ffmpeg_noise/binary_sensor.py index 7efcc3deda2637..7fbda8ca18b619 100644 --- a/homeassistant/components/ffmpeg_noise/binary_sensor.py +++ b/homeassistant/components/ffmpeg_noise/binary_sensor.py @@ -12,8 +12,6 @@ CONF_INITIAL_STATE) from homeassistant.const import CONF_NAME -DEPENDENCIES = ['ffmpeg'] - _LOGGER = logging.getLogger(__name__) CONF_PEAK = 'peak' diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index 6b37b178a595bb..9e60d1c0c3ab44 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import convert, slugify -REQUIREMENTS = ['fiblary3==0.1.7'] - _LOGGER = logging.getLogger(__name__) ATTR_CURRENT_ENERGY_KWH = 'current_energy_kwh' diff --git a/homeassistant/components/fibaro/binary_sensor.py b/homeassistant/components/fibaro/binary_sensor.py index f71a5f3662e8e2..44448227a1c24f 100644 --- a/homeassistant/components/fibaro/binary_sensor.py +++ b/homeassistant/components/fibaro/binary_sensor.py @@ -7,8 +7,6 @@ from . import FIBARO_DEVICES, FibaroDevice -DEPENDENCIES = ['fibaro'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index 0d1ecc3a77f9cf..4b12a907ce325d 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -44,8 +44,6 @@ FAN_UP_DOWN = 'up_down' FAN_QUIET = 'quiet' -DEPENDENCIES = ['fibaro'] - _LOGGER = logging.getLogger(__name__) # SDS13781-10 Z-Wave Application Command Class Specification 2019-01-04 diff --git a/homeassistant/components/fibaro/cover.py b/homeassistant/components/fibaro/cover.py index 0f5cc32bc9695b..0ccbed0144b387 100644 --- a/homeassistant/components/fibaro/cover.py +++ b/homeassistant/components/fibaro/cover.py @@ -6,8 +6,6 @@ from . import FIBARO_DEVICES, FibaroDevice -DEPENDENCIES = ['fibaro'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index 600b566b36b23c..a741de972f054f 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['fibaro'] - def scaleto255(value): """Scale the input value from 0-100 to 0-255.""" diff --git a/homeassistant/components/fibaro/scene.py b/homeassistant/components/fibaro/scene.py index 93f0cd5b63afd0..f9f96844319b94 100644 --- a/homeassistant/components/fibaro/scene.py +++ b/homeassistant/components/fibaro/scene.py @@ -5,8 +5,6 @@ from . import FIBARO_DEVICES, FibaroDevice -DEPENDENCIES = ['fibaro'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fibaro/sensor.py b/homeassistant/components/fibaro/sensor.py index 20a37fd3c23756..db9d103d87eb6c 100644 --- a/homeassistant/components/fibaro/sensor.py +++ b/homeassistant/components/fibaro/sensor.py @@ -22,7 +22,6 @@ ['Light', 'lx', None, DEVICE_CLASS_ILLUMINANCE] } -DEPENDENCIES = ['fibaro'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fibaro/switch.py b/homeassistant/components/fibaro/switch.py index 024531f62c70e8..f134b424484de6 100644 --- a/homeassistant/components/fibaro/switch.py +++ b/homeassistant/components/fibaro/switch.py @@ -6,7 +6,6 @@ from . import FIBARO_DEVICES, FibaroDevice -DEPENDENCIES = ['fibaro'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fido/sensor.py b/homeassistant/components/fido/sensor.py index 00754c5ba68c12..ea66acaf808ecf 100644 --- a/homeassistant/components/fido/sensor.py +++ b/homeassistant/components/fido/sensor.py @@ -20,8 +20,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyfido==2.1.1'] - _LOGGER = logging.getLogger(__name__) KILOBITS = 'Kb' # type: str diff --git a/homeassistant/components/fints/sensor.py b/homeassistant/components/fints/sensor.py index dce52785fbf269..cb993ada8dade7 100644 --- a/homeassistant/components/fints/sensor.py +++ b/homeassistant/components/fints/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['fints==1.0.1'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(hours=4) diff --git a/homeassistant/components/fitbit/sensor.py b/homeassistant/components/fitbit/sensor.py index abbe69c3e1d7db..889920239edbb9 100644 --- a/homeassistant/components/fitbit/sensor.py +++ b/homeassistant/components/fitbit/sensor.py @@ -17,8 +17,6 @@ from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['fitbit==0.3.0'] - _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) @@ -32,8 +30,6 @@ CONF_CLOCK_FORMAT = 'clock_format' ATTRIBUTION = 'Data provided by Fitbit.com' -DEPENDENCIES = ['http'] - FITBIT_AUTH_CALLBACK_PATH = '/api/fitbit/callback' FITBIT_AUTH_START = '/api/fitbit' FITBIT_CONFIG_FILE = 'fitbit.conf' diff --git a/homeassistant/components/fixer/sensor.py b/homeassistant/components/fixer/sensor.py index f746d2008e10f3..4cf2b0b9243260 100644 --- a/homeassistant/components/fixer/sensor.py +++ b/homeassistant/components/fixer/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['fixerio==1.0.0a0'] - _LOGGER = logging.getLogger(__name__) ATTR_EXCHANGE_RATE = 'Exchange rate' diff --git a/homeassistant/components/flexit/climate.py b/homeassistant/components/flexit/climate.py index fe7b5ff8e7cd2f..d1cf97f047a277 100644 --- a/homeassistant/components/flexit/climate.py +++ b/homeassistant/components/flexit/climate.py @@ -25,9 +25,6 @@ CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyflexit==0.3'] -DEPENDENCIES = ['modbus'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HUB, default=DEFAULT_HUB): cv.string, vol.Required(CONF_SLAVE): vol.All(int, vol.Range(min=0, max=32)), diff --git a/homeassistant/components/flic/binary_sensor.py b/homeassistant/components/flic/binary_sensor.py index 083ac01ab4a2d9..3381550b5781eb 100644 --- a/homeassistant/components/flic/binary_sensor.py +++ b/homeassistant/components/flic/binary_sensor.py @@ -11,8 +11,6 @@ from homeassistant.components.binary_sensor import ( BinarySensorDevice, PLATFORM_SCHEMA) -REQUIREMENTS = ['pyflic-homeassistant==0.4.dev0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_TIMEOUT = 3 diff --git a/homeassistant/components/flunearyou/sensor.py b/homeassistant/components/flunearyou/sensor.py index 65de2c6ae43e0d..148a3ee41592d2 100644 --- a/homeassistant/components/flunearyou/sensor.py +++ b/homeassistant/components/flunearyou/sensor.py @@ -13,7 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pyflunearyou==1.0.3'] _LOGGER = logging.getLogger(__name__) ATTR_CITY = 'city' diff --git a/homeassistant/components/flux/switch.py b/homeassistant/components/flux/switch.py index fdd0c09b9d7a60..f0134f04d890dc 100644 --- a/homeassistant/components/flux/switch.py +++ b/homeassistant/components/flux/switch.py @@ -42,8 +42,6 @@ MODE_MIRED = 'mired' MODE_RGB = 'rgb' DEFAULT_MODE = MODE_XY -DEPENDENCIES = ['light'] - PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): 'flux', diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 0ed14c49ec851c..56d088f20788da 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['flux_led==0.22'] - _LOGGER = logging.getLogger(__name__) CONF_AUTOMATIC_ADD = 'automatic_add' diff --git a/homeassistant/components/folder_watcher/__init__.py b/homeassistant/components/folder_watcher/__init__.py index babfbd9e9aa61c..411f6b480dcb8c 100644 --- a/homeassistant/components/folder_watcher/__init__.py +++ b/homeassistant/components/folder_watcher/__init__.py @@ -8,8 +8,6 @@ EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['watchdog==0.8.3'] - _LOGGER = logging.getLogger(__name__) CONF_FOLDER = 'folder' diff --git a/homeassistant/components/foobot/sensor.py b/homeassistant/components/foobot/sensor.py index 2eeca5243a66f5..f59392bde985b6 100644 --- a/homeassistant/components/foobot/sensor.py +++ b/homeassistant/components/foobot/sensor.py @@ -16,8 +16,6 @@ from homeassistant.util import Throttle -REQUIREMENTS = ['foobot_async==0.3.1'] - _LOGGER = logging.getLogger(__name__) ATTR_HUMIDITY = 'humidity' diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 6ce8f1865fcf77..f83c3f1966ae7e 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['libpyfoscam==1.0'] - CONF_IP = 'ip' CONF_RTSP_PORT = 'rtsp_port' diff --git a/homeassistant/components/foursquare/__init__.py b/homeassistant/components/foursquare/__init__.py index 0c5a48049ecc9e..dd8349998886e5 100644 --- a/homeassistant/components/foursquare/__init__.py +++ b/homeassistant/components/foursquare/__init__.py @@ -12,7 +12,6 @@ CONF_PUSH_SECRET = 'push_secret' -DEPENDENCIES = ['http'] DOMAIN = 'foursquare' EVENT_CHECKIN = 'foursquare.checkin' diff --git a/homeassistant/components/free_mobile/notify.py b/homeassistant/components/free_mobile/notify.py index 03beef52357af6..c7dacd44019d48 100644 --- a/homeassistant/components/free_mobile/notify.py +++ b/homeassistant/components/free_mobile/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import (PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['freesms==0.1.2'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/freebox/__init__.py b/homeassistant/components/freebox/__init__.py index 7accf7820f4d14..2cd9f6b35721d7 100644 --- a/homeassistant/components/freebox/__init__.py +++ b/homeassistant/components/freebox/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['aiofreepybox==0.0.8'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "freebox" diff --git a/homeassistant/components/freebox/device_tracker.py b/homeassistant/components/freebox/device_tracker.py index 5418c1c61a7322..40c1967f60f6e4 100644 --- a/homeassistant/components/freebox/device_tracker.py +++ b/homeassistant/components/freebox/device_tracker.py @@ -6,8 +6,6 @@ from . import DATA_FREEBOX -DEPENDENCIES = ['freebox'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/freebox/sensor.py b/homeassistant/components/freebox/sensor.py index 328665ab51cd9b..8dcc5f54b2e67e 100644 --- a/homeassistant/components/freebox/sensor.py +++ b/homeassistant/components/freebox/sensor.py @@ -5,8 +5,6 @@ from . import DATA_FREEBOX -DEPENDENCIES = ['freebox'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/freebox/switch.py b/homeassistant/components/freebox/switch.py index 4de194fc9023fd..e0c24d2b9f9f21 100644 --- a/homeassistant/components/freebox/switch.py +++ b/homeassistant/components/freebox/switch.py @@ -5,8 +5,6 @@ from . import DATA_FREEBOX -DEPENDENCIES = ['freebox'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fritz/device_tracker.py b/homeassistant/components/fritz/device_tracker.py index 3e3e04f44475fb..fc9f65633ff86e 100644 --- a/homeassistant/components/fritz/device_tracker.py +++ b/homeassistant/components/fritz/device_tracker.py @@ -8,8 +8,6 @@ DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME -REQUIREMENTS = ['fritzconnection==0.6.5'] - _LOGGER = logging.getLogger(__name__) CONF_DEFAULT_IP = '169.254.1.1' # This IP is valid for all FRITZ!Box routers. diff --git a/homeassistant/components/fritzbox/__init__.py b/homeassistant/components/fritzbox/__init__.py index 81ba019acbc0b0..610c68741405b5 100644 --- a/homeassistant/components/fritzbox/__init__.py +++ b/homeassistant/components/fritzbox/__init__.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyfritzhome==0.4.0'] - SUPPORTED_DOMAINS = ['binary_sensor', 'climate', 'switch', 'sensor'] DOMAIN = 'fritzbox' diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index 65578c571805e9..a763a3b3b0e4c2 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -7,8 +7,6 @@ from . import DOMAIN as FRITZBOX_DOMAIN -DEPENDENCIES = ['fritzbox'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index e2c9be833ac4d3..4dfa09c49fa960 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -16,8 +16,6 @@ ATTR_STATE_LOCKED, ATTR_STATE_SUMMER_MODE, ATTR_STATE_WINDOW_OPEN, DOMAIN as FRITZBOX_DOMAIN) -DEPENDENCIES = ['fritzbox'] - _LOGGER = logging.getLogger(__name__) SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE) diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index 7309f8cc6180a6..123d8835318166 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -9,8 +9,6 @@ from . import ( ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED, DOMAIN as FRITZBOX_DOMAIN) -DEPENDENCIES = ['fritzbox'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py index e227cdaef8a00a..ae1219cefda3ce 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -10,8 +10,6 @@ from . import ( ATTR_STATE_DEVICE_LOCKED, ATTR_STATE_LOCKED, DOMAIN as FRITZBOX_DOMAIN) -DEPENDENCIES = ['fritzbox'] - _LOGGER = logging.getLogger(__name__) ATTR_TOTAL_CONSUMPTION = 'total_consumption' diff --git a/homeassistant/components/fritzbox_callmonitor/sensor.py b/homeassistant/components/fritzbox_callmonitor/sensor.py index a6641bc14ad5b5..95c0879996f5ed 100644 --- a/homeassistant/components/fritzbox_callmonitor/sensor.py +++ b/homeassistant/components/fritzbox_callmonitor/sensor.py @@ -16,8 +16,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['fritzconnection==0.6.5'] - _LOGGER = logging.getLogger(__name__) CONF_PHONEBOOK = 'phonebook' diff --git a/homeassistant/components/fritzbox_netmonitor/sensor.py b/homeassistant/components/fritzbox_netmonitor/sensor.py index 93f834a894d3b7..ec8e38bb24ba56 100644 --- a/homeassistant/components/fritzbox_netmonitor/sensor.py +++ b/homeassistant/components/fritzbox_netmonitor/sensor.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['fritzconnection==0.6.5'] - _LOGGER = logging.getLogger(__name__) CONF_DEFAULT_NAME = 'fritz_netmonitor' diff --git a/homeassistant/components/fritzdect/switch.py b/homeassistant/components/fritzdect/switch.py index 449ae5a76f13ae..d3cd00a73f5762 100644 --- a/homeassistant/components/fritzdect/switch.py +++ b/homeassistant/components/fritzdect/switch.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE -REQUIREMENTS = ['fritzhome==1.0.4'] - _LOGGER = logging.getLogger(__name__) # Standard Fritz Box IP diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index cfee41dc6ae806..6f258b2d59c865 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,12 +21,7 @@ from .storage import async_setup_frontend_storage -REQUIREMENTS = ['home-assistant-frontend==20190410.0'] - DOMAIN = 'frontend' -DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', - 'auth', 'onboarding', 'lovelace'] - CONF_THEMES = 'themes' CONF_EXTRA_HTML_URL = 'extra_html_url' CONF_EXTRA_HTML_URL_ES5 = 'extra_html_url_es5' diff --git a/homeassistant/components/frontier_silicon/media_player.py b/homeassistant/components/frontier_silicon/media_player.py index 4f28d83e6cfe36..64aa1d3a012617 100644 --- a/homeassistant/components/frontier_silicon/media_player.py +++ b/homeassistant/components/frontier_silicon/media_player.py @@ -15,8 +15,6 @@ STATE_PLAYING, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['afsapi==0.0.4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_FRONTIER_SILICON = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \ diff --git a/homeassistant/components/futurenow/light.py b/homeassistant/components/futurenow/light.py index 4b570fd0a4dcdb..91ec8b0794d540 100644 --- a/homeassistant/components/futurenow/light.py +++ b/homeassistant/components/futurenow/light.py @@ -11,8 +11,6 @@ PLATFORM_SCHEMA) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyfnip==0.2'] - _LOGGER = logging.getLogger(__name__) CONF_DRIVER = 'driver' diff --git a/homeassistant/components/gc100/__init__.py b/homeassistant/components/gc100/__init__.py index 36e9c61b1ba1e1..b875d045cc09db 100644 --- a/homeassistant/components/gc100/__init__.py +++ b/homeassistant/components/gc100/__init__.py @@ -7,8 +7,6 @@ EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-gc100==1.0.3a'] - _LOGGER = logging.getLogger(__name__) CONF_PORTS = 'ports' diff --git a/homeassistant/components/gc100/binary_sensor.py b/homeassistant/components/gc100/binary_sensor.py index 9588506af77666..4ba68a17799655 100644 --- a/homeassistant/components/gc100/binary_sensor.py +++ b/homeassistant/components/gc100/binary_sensor.py @@ -8,8 +8,6 @@ from . import CONF_PORTS, DATA_GC100 -DEPENDENCIES = ['gc100'] - _SENSORS_SCHEMA = vol.Schema({ cv.string: cv.string, }) diff --git a/homeassistant/components/gc100/switch.py b/homeassistant/components/gc100/switch.py index 1ffb2726495ffc..eea98a4dc23c61 100644 --- a/homeassistant/components/gc100/switch.py +++ b/homeassistant/components/gc100/switch.py @@ -8,8 +8,6 @@ from . import CONF_PORTS, DATA_GC100 -DEPENDENCIES = ['gc100'] - _SWITCH_SCHEMA = vol.Schema({ cv.string: cv.string, }) diff --git a/homeassistant/components/gearbest/sensor.py b/homeassistant/components/gearbest/sensor.py index e4f85a1892dfc7..ee0ee6d4e3bffb 100644 --- a/homeassistant/components/gearbest/sensor.py +++ b/homeassistant/components/gearbest/sensor.py @@ -11,7 +11,6 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.const import (CONF_NAME, CONF_ID, CONF_URL, CONF_CURRENCY) -REQUIREMENTS = ['gearbest_parser==1.0.7'] _LOGGER = logging.getLogger(__name__) CONF_ITEMS = 'items' diff --git a/homeassistant/components/geizhals/sensor.py b/homeassistant/components/geizhals/sensor.py index d619d768c234e3..03c263f54ab1d5 100644 --- a/homeassistant/components/geizhals/sensor.py +++ b/homeassistant/components/geizhals/sensor.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.const import CONF_NAME -REQUIREMENTS = ['geizhals==0.0.9'] - _LOGGER = logging.getLogger(__name__) CONF_DESCRIPTION = 'description' diff --git a/homeassistant/components/generic_thermostat/climate.py b/homeassistant/components/generic_thermostat/climate.py index 35efa82c8a3c1f..cfa8ba64ea5e7e 100644 --- a/homeassistant/components/generic_thermostat/climate.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -23,8 +23,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['switch', 'sensor'] - DEFAULT_TOLERANCE = 0.3 DEFAULT_NAME = 'Generic Thermostat' diff --git a/homeassistant/components/geo_json_events/geo_location.py b/homeassistant/components/geo_json_events/geo_location.py index e89616126d5f24..f7d79ae7145230 100644 --- a/homeassistant/components/geo_json_events/geo_location.py +++ b/homeassistant/components/geo_json_events/geo_location.py @@ -16,8 +16,6 @@ async_dispatcher_connect, dispatcher_send) from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['geojson_client==0.3'] - _LOGGER = logging.getLogger(__name__) ATTR_EXTERNAL_ID = 'external_id' diff --git a/homeassistant/components/geo_rss_events/sensor.py b/homeassistant/components/geo_rss_events/sensor.py index f71a60c2e83e89..f900812385b00d 100644 --- a/homeassistant/components/geo_rss_events/sensor.py +++ b/homeassistant/components/geo_rss_events/sensor.py @@ -20,8 +20,6 @@ CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS, CONF_URL) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['georss_generic_client==0.2'] - _LOGGER = logging.getLogger(__name__) ATTR_CATEGORY = 'category' diff --git a/homeassistant/components/geofency/__init__.py b/homeassistant/components/geofency/__init__.py index 88b72f02cc2e9f..0b4b757ce9efb0 100644 --- a/homeassistant/components/geofency/__init__.py +++ b/homeassistant/components/geofency/__init__.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'geofency' -DEPENDENCIES = ['webhook'] - CONF_MOBILE_BEACONS = 'mobile_beacons' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/geofency/device_tracker.py b/homeassistant/components/geofency/device_tracker.py index 0a1a9d5f32edc6..abccf610f5e44f 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['geofency'] - DATA_KEY = '{}.{}'.format(GEOFENCY_DOMAIN, DEVICE_TRACKER_DOMAIN) diff --git a/homeassistant/components/github/sensor.py b/homeassistant/components/github/sensor.py index 5a86233d561ff7..d552d2c65ccc2f 100644 --- a/homeassistant/components/github/sensor.py +++ b/homeassistant/components/github/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['PyGithub==1.43.5'] - _LOGGER = logging.getLogger(__name__) CONF_REPOS = 'repositories' diff --git a/homeassistant/components/gitlab_ci/sensor.py b/homeassistant/components/gitlab_ci/sensor.py index dd574b348d8239..54cbf34fdfc2c8 100644 --- a/homeassistant/components/gitlab_ci/sensor.py +++ b/homeassistant/components/gitlab_ci/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['python-gitlab==1.6.0'] - _LOGGER = logging.getLogger(__name__) ATTR_BUILD_BRANCH = 'build branch' diff --git a/homeassistant/components/gitter/sensor.py b/homeassistant/components/gitter/sensor.py index 2af9c20fb29094..06fb6e3a3b5440 100644 --- a/homeassistant/components/gitter/sensor.py +++ b/homeassistant/components/gitter/sensor.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['gitterpy==0.1.7'] - _LOGGER = logging.getLogger(__name__) ATTR_MENTION = 'mention' diff --git a/homeassistant/components/glances/sensor.py b/homeassistant/components/glances/sensor.py index db8f0397887e89..2a883e33da68d7 100644 --- a/homeassistant/components/glances/sensor.py +++ b/homeassistant/components/glances/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['glances_api==0.2.0'] - _LOGGER = logging.getLogger(__name__) CONF_VERSION = 'version' diff --git a/homeassistant/components/gntp/notify.py b/homeassistant/components/gntp/notify.py index fb3e96e83ab827..005043c1384941 100644 --- a/homeassistant/components/gntp/notify.py +++ b/homeassistant/components/gntp/notify.py @@ -10,8 +10,6 @@ from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['gntp==1.0.3'] - _LOGGER = logging.getLogger(__name__) _GNTP_LOGGER = logging.getLogger('gntp') diff --git a/homeassistant/components/goalfeed/__init__.py b/homeassistant/components/goalfeed/__init__.py index 6f0149f657a908..4a7e4ea980a4c5 100644 --- a/homeassistant/components/goalfeed/__init__.py +++ b/homeassistant/components/goalfeed/__init__.py @@ -9,7 +9,6 @@ # Version downgraded due to regression in library # For details: https://github.com/nlsdfnbch/Pysher/issues/38 -REQUIREMENTS = ['pysher==1.0.1'] DOMAIN = 'goalfeed' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/gogogate2/cover.py b/homeassistant/components/gogogate2/cover.py index 4d40ddd2c72ee9..610c131bda5bcd 100644 --- a/homeassistant/components/gogogate2/cover.py +++ b/homeassistant/components/gogogate2/cover.py @@ -10,8 +10,6 @@ CONF_IP_ADDRESS, CONF_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pygogogate2==0.1.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'gogogate2' diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 0216094de9b872..e9bbf3f96cdd9f 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -13,12 +13,6 @@ from homeassistant.helpers.event import track_time_change from homeassistant.util import convert, dt -REQUIREMENTS = [ - 'google-api-python-client==1.6.4', - 'httplib2==0.10.3', - 'oauth2client==4.0.0', -] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'google' diff --git a/homeassistant/components/google/tts.py b/homeassistant/components/google/tts.py index 49a945cbbfd241..4d988bed21cfc7 100644 --- a/homeassistant/components/google/tts.py +++ b/homeassistant/components/google/tts.py @@ -12,8 +12,6 @@ from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['gTTS-token==1.1.3'] - _LOGGER = logging.getLogger(__name__) GOOGLE_SPEECH_URL = "https://translate.google.com/translate_tts" diff --git a/homeassistant/components/google_assistant/__init__.py b/homeassistant/components/google_assistant/__init__.py index 0fd167c2729436..2d3a19afa1302a 100644 --- a/homeassistant/components/google_assistant/__init__.py +++ b/homeassistant/components/google_assistant/__init__.py @@ -28,8 +28,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] - ENTITY_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_EXPOSE): cv.boolean, diff --git a/homeassistant/components/google_maps/device_tracker.py b/homeassistant/components/google_maps/device_tracker.py index 7bc9be00b8cc66..5788392190aa8f 100644 --- a/homeassistant/components/google_maps/device_tracker.py +++ b/homeassistant/components/google_maps/device_tracker.py @@ -14,8 +14,6 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.util import slugify, dt as dt_util -REQUIREMENTS = ['locationsharinglib==3.0.11'] - _LOGGER = logging.getLogger(__name__) ATTR_ADDRESS = 'address' diff --git a/homeassistant/components/google_pubsub/__init__.py b/homeassistant/components/google_pubsub/__init__.py index 18c068ea454bcc..8aaa7a17ac44c4 100644 --- a/homeassistant/components/google_pubsub/__init__.py +++ b/homeassistant/components/google_pubsub/__init__.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['google-cloud-pubsub==0.39.1'] - DOMAIN = 'google_pubsub' CONF_PROJECT_ID = 'project_id' diff --git a/homeassistant/components/google_travel_time/sensor.py b/homeassistant/components/google_travel_time/sensor.py index b448830ab02aa6..ef4fc76f53ea14 100644 --- a/homeassistant/components/google_travel_time/sensor.py +++ b/homeassistant/components/google_travel_time/sensor.py @@ -15,8 +15,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['googlemaps==2.5.1'] - _LOGGER = logging.getLogger(__name__) CONF_DESTINATION = 'destination' diff --git a/homeassistant/components/googlehome/__init__.py b/homeassistant/components/googlehome/__init__.py index 6ebc2f512b1fef..073081a9634285 100644 --- a/homeassistant/components/googlehome/__init__.py +++ b/homeassistant/components/googlehome/__init__.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['googledevices==1.0.2'] - DOMAIN = 'googlehome' CLIENT = 'googlehome_client' diff --git a/homeassistant/components/googlehome/device_tracker.py b/homeassistant/components/googlehome/device_tracker.py index c024cde0c6ce66..3b6bc5d341c6ce 100644 --- a/homeassistant/components/googlehome/device_tracker.py +++ b/homeassistant/components/googlehome/device_tracker.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['googlehome'] - DEFAULT_SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/googlehome/sensor.py b/homeassistant/components/googlehome/sensor.py index 4f37740da85ed2..088f4352fa3a87 100644 --- a/homeassistant/components/googlehome/sensor.py +++ b/homeassistant/components/googlehome/sensor.py @@ -8,8 +8,6 @@ from . import CLIENT, DOMAIN as GOOGLEHOME_DOMAIN, NAME -DEPENDENCIES = ['googlehome'] - SCAN_INTERVAL = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/gpmdp/media_player.py b/homeassistant/components/gpmdp/media_player.py index 788126b957f9fc..76253d32db837a 100644 --- a/homeassistant/components/gpmdp/media_player.py +++ b/homeassistant/components/gpmdp/media_player.py @@ -16,8 +16,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['websocket-client==0.54.0'] - _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/gpsd/sensor.py b/homeassistant/components/gpsd/sensor.py index 62307cb1011631..cccf59a822a34e 100644 --- a/homeassistant/components/gpsd/sensor.py +++ b/homeassistant/components/gpsd/sensor.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['gps3==0.33.3'] - _LOGGER = logging.getLogger(__name__) ATTR_CLIMB = 'climb' diff --git a/homeassistant/components/gpslogger/__init__.py b/homeassistant/components/gpslogger/__init__.py index 6bc9d11a68e088..6887b85d02d689 100644 --- a/homeassistant/components/gpslogger/__init__.py +++ b/homeassistant/components/gpslogger/__init__.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'gpslogger' -DEPENDENCIES = ['webhook'] - TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN) ATTR_ALTITUDE = 'altitude' diff --git a/homeassistant/components/gpslogger/device_tracker.py b/homeassistant/components/gpslogger/device_tracker.py index c9496975272811..67967821083514 100644 --- a/homeassistant/components/gpslogger/device_tracker.py +++ b/homeassistant/components/gpslogger/device_tracker.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['gpslogger'] - DATA_KEY = '{}.{}'.format(GPSLOGGER_DOMAIN, DEVICE_TRACKER_DOMAIN) diff --git a/homeassistant/components/greeneye_monitor/__init__.py b/homeassistant/components/greeneye_monitor/__init__.py index aedc98aac314e6..0f12c3cd47945a 100644 --- a/homeassistant/components/greeneye_monitor/__init__.py +++ b/homeassistant/components/greeneye_monitor/__init__.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['greeneye_monitor==1.0'] - _LOGGER = logging.getLogger(__name__) CONF_CHANNELS = 'channels' diff --git a/homeassistant/components/greeneye_monitor/sensor.py b/homeassistant/components/greeneye_monitor/sensor.py index 8321bb768cabf0..ddfa5c1504b005 100644 --- a/homeassistant/components/greeneye_monitor/sensor.py +++ b/homeassistant/components/greeneye_monitor/sensor.py @@ -23,8 +23,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['greeneye_monitor'] - DATA_PULSES = 'pulses' DATA_WATT_SECONDS = 'watt_seconds' diff --git a/homeassistant/components/greenwave/light.py b/homeassistant/components/greenwave/light.py index b8efe8ae17dcba..a8418a01ac2ee0 100644 --- a/homeassistant/components/greenwave/light.py +++ b/homeassistant/components/greenwave/light.py @@ -10,7 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['greenwavereality==0.5.1'] _LOGGER = logging.getLogger(__name__) CONF_VERSION = 'version' diff --git a/homeassistant/components/gstreamer/media_player.py b/homeassistant/components/gstreamer/media_player.py index 094a561d310ea5..f74040105130f4 100644 --- a/homeassistant/components/gstreamer/media_player.py +++ b/homeassistant/components/gstreamer/media_player.py @@ -11,8 +11,6 @@ from homeassistant.const import CONF_NAME, EVENT_HOMEASSISTANT_STOP, STATE_IDLE import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['gstreamer-player==1.1.2'] - _LOGGER = logging.getLogger(__name__) CONF_PIPELINE = 'pipeline' diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 9e89a8ad844de3..0a9301f8c33378 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -17,8 +17,6 @@ from homeassistant.util import slugify import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pygtfs==0.1.5'] - _LOGGER = logging.getLogger(__name__) ATTR_ARRIVAL = 'arrival' diff --git a/homeassistant/components/gtt/sensor.py b/homeassistant/components/gtt/sensor.py index 659984fadea8f9..ecabd5f0a718ac 100644 --- a/homeassistant/components/gtt/sensor.py +++ b/homeassistant/components/gtt/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pygtt==1.1.2'] - _LOGGER = logging.getLogger(__name__) CONF_STOP = 'stop' diff --git a/homeassistant/components/habitica/__init__.py b/homeassistant/components/habitica/__init__.py index 23113a1388b4f7..611e8df006ae13 100644 --- a/homeassistant/components/habitica/__init__.py +++ b/homeassistant/components/habitica/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['habitipy==0.2.0'] - _LOGGER = logging.getLogger(__name__) CONF_API_USER = 'api_user' diff --git a/homeassistant/components/hangouts/__init__.py b/homeassistant/components/hangouts/__init__.py index 29cdc29e5ada6c..50936ac62a060f 100644 --- a/homeassistant/components/hangouts/__init__.py +++ b/homeassistant/components/hangouts/__init__.py @@ -19,8 +19,6 @@ MESSAGE_SCHEMA, SERVICE_RECONNECT, SERVICE_SEND_MESSAGE, SERVICE_UPDATE, TARGETS_SCHEMA) -REQUIREMENTS = ['hangups==0.4.9'] - _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/hangouts/notify.py b/homeassistant/components/hangouts/notify.py index de9af2e077512b..e88f80afbcde29 100644 --- a/homeassistant/components/hangouts/notify.py +++ b/homeassistant/components/hangouts/notify.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = [DOMAIN] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_DEFAULT_CONVERSATIONS): [TARGETS_SCHEMA] }) diff --git a/homeassistant/components/harman_kardon_avr/media_player.py b/homeassistant/components/harman_kardon_avr/media_player.py index cec0ac4f5c8653..dc200f39b9c8af 100644 --- a/homeassistant/components/harman_kardon_avr/media_player.py +++ b/homeassistant/components/harman_kardon_avr/media_player.py @@ -12,8 +12,6 @@ from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PORT, STATE_OFF, STATE_ON) -REQUIREMENTS = ['hkavr==0.0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Harman Kardon AVR' diff --git a/homeassistant/components/harmony/remote.py b/homeassistant/components/harmony/remote.py index 12b3a91e12b675..c4aebb1bdcbb4a 100644 --- a/homeassistant/components/harmony/remote.py +++ b/homeassistant/components/harmony/remote.py @@ -16,8 +16,6 @@ from homeassistant.exceptions import PlatformNotReady from homeassistant.util import slugify -REQUIREMENTS = ['aioharmony==0.1.11'] - _LOGGER = logging.getLogger(__name__) ATTR_CHANNEL = 'channel' diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index e8d04b1596d980..2fdb859c320934 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -25,7 +25,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'hassio' -DEPENDENCIES = ['http'] STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 diff --git a/homeassistant/components/hdmi_cec/__init__.py b/homeassistant/components/hdmi_cec/__init__.py index 8eb13c5ab213ae..189cc748d5d994 100644 --- a/homeassistant/components/hdmi_cec/__init__.py +++ b/homeassistant/components/hdmi_cec/__init__.py @@ -17,8 +17,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyCEC==0.4.13'] - DOMAIN = 'hdmi_cec' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hdmi_cec/media_player.py b/homeassistant/components/hdmi_cec/media_player.py index b2d2910e145bbe..4468fd9d648dc8 100644 --- a/homeassistant/components/hdmi_cec/media_player.py +++ b/homeassistant/components/hdmi_cec/media_player.py @@ -11,8 +11,6 @@ from . import ATTR_NEW, CecDevice -DEPENDENCIES = ['hdmi_cec'] - _LOGGER = logging.getLogger(__name__) ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index 639f545707ee2b..9fb003f6d6a015 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -6,8 +6,6 @@ from . import ATTR_NEW, CecDevice -DEPENDENCIES = ['hdmi_cec'] - _LOGGER = logging.getLogger(__name__) ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/heatmiser/climate.py b/homeassistant/components/heatmiser/climate.py index fc9057bc905699..045ffdd34c586a 100644 --- a/homeassistant/components/heatmiser/climate.py +++ b/homeassistant/components/heatmiser/climate.py @@ -10,8 +10,6 @@ TEMP_CELSIUS, ATTR_TEMPERATURE, CONF_PORT, CONF_NAME, CONF_ID) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['heatmiserV3==0.9.1'] - _LOGGER = logging.getLogger(__name__) CONF_IPADDRESS = 'ipaddress' diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index 084444be4ea87f..529ee27997ed50 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -19,8 +19,6 @@ COMMAND_RETRY_ATTEMPTS, COMMAND_RETRY_DELAY, DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) -REQUIREMENTS = ['pyheos==0.3.1'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_HOST): cv.string diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 72d42f8f66f6bb..0da9db31bb28f6 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -17,8 +17,6 @@ from .const import ( DATA_SOURCE_MANAGER, DOMAIN as HEOS_DOMAIN, SIGNAL_HEOS_SOURCES_UPDATED) -DEPENDENCIES = ['heos'] - BASE_SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ SUPPORT_VOLUME_STEP | SUPPORT_CLEAR_PLAYLIST | \ SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOURCE diff --git a/homeassistant/components/hikvision/binary_sensor.py b/homeassistant/components/hikvision/binary_sensor.py index a6a82c9ee1b814..f15d67396151b4 100644 --- a/homeassistant/components/hikvision/binary_sensor.py +++ b/homeassistant/components/hikvision/binary_sensor.py @@ -13,7 +13,6 @@ CONF_SSL, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START, ATTR_LAST_TRIP_TIME, CONF_CUSTOMIZE) -REQUIREMENTS = ['pyhik==0.2.2'] _LOGGER = logging.getLogger(__name__) CONF_IGNORED = 'ignored' diff --git a/homeassistant/components/hikvisioncam/switch.py b/homeassistant/components/hikvisioncam/switch.py index 6e5dcdac9aae6b..373f84cee0e3a5 100644 --- a/homeassistant/components/hikvisioncam/switch.py +++ b/homeassistant/components/hikvisioncam/switch.py @@ -10,7 +10,6 @@ from homeassistant.helpers.entity import ToggleEntity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['hikvision==0.4'] # This is the last working version, please test before updating _LOGGING = logging.getLogger(__name__) diff --git a/homeassistant/components/hipchat/notify.py b/homeassistant/components/hipchat/notify.py index 5128b8beea341c..f12fd1ffa76e12 100644 --- a/homeassistant/components/hipchat/notify.py +++ b/homeassistant/components/hipchat/notify.py @@ -10,8 +10,6 @@ PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['hipnotify==1.0.8'] - _LOGGER = logging.getLogger(__name__) CONF_COLOR = 'color' diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index 7b07fac19a692d..7efe4f2beb20ab 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -19,8 +19,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'history' -DEPENDENCIES = ['recorder', 'http'] - CONF_ORDER = 'use_include_order' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/history_graph/__init__.py b/homeassistant/components/history_graph/__init__.py index 893f3514d77f93..964d47d25025d3 100644 --- a/homeassistant/components/history_graph/__init__.py +++ b/homeassistant/components/history_graph/__init__.py @@ -8,8 +8,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent -DEPENDENCIES = ['history'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'history_graph' diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index f1eea4dd693086..a0a08d4833e2b1 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -20,8 +20,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'history_stats' -DEPENDENCIES = ['history'] - CONF_START = 'start' CONF_END = 'end' CONF_DURATION = 'duration' diff --git a/homeassistant/components/hive/__init__.py b/homeassistant/components/hive/__init__.py index 934c44028ac27b..fdda1f1f5426cd 100644 --- a/homeassistant/components/hive/__init__.py +++ b/homeassistant/components/hive/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform -REQUIREMENTS = ['pyhiveapi==0.2.17'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'hive' diff --git a/homeassistant/components/hive/binary_sensor.py b/homeassistant/components/hive/binary_sensor.py index a0973f4d8e93a7..97900c2852e277 100644 --- a/homeassistant/components/hive/binary_sensor.py +++ b/homeassistant/components/hive/binary_sensor.py @@ -3,8 +3,6 @@ from . import DATA_HIVE, DOMAIN -DEPENDENCIES = ['hive'] - DEVICETYPE_DEVICE_CLASS = { 'motionsensor': 'motion', 'contactsensor': 'opening', diff --git a/homeassistant/components/hive/climate.py b/homeassistant/components/hive/climate.py index dac7feb2927fe6..ab9b63dad6094f 100644 --- a/homeassistant/components/hive/climate.py +++ b/homeassistant/components/hive/climate.py @@ -8,8 +8,6 @@ from . import DATA_HIVE, DOMAIN -DEPENDENCIES = ['hive'] - HIVE_TO_HASS_STATE = { 'SCHEDULE': STATE_AUTO, 'MANUAL': STATE_HEAT, diff --git a/homeassistant/components/hive/light.py b/homeassistant/components/hive/light.py index 3a2176c3eedab7..67331b12b35c4b 100644 --- a/homeassistant/components/hive/light.py +++ b/homeassistant/components/hive/light.py @@ -6,8 +6,6 @@ from . import DATA_HIVE, DOMAIN -DEPENDENCIES = ['hive'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive light devices.""" diff --git a/homeassistant/components/hive/sensor.py b/homeassistant/components/hive/sensor.py index e7b7d6b45977aa..b8887d27409b28 100644 --- a/homeassistant/components/hive/sensor.py +++ b/homeassistant/components/hive/sensor.py @@ -4,8 +4,6 @@ from . import DATA_HIVE, DOMAIN -DEPENDENCIES = ['hive'] - FRIENDLY_NAMES = { 'Hub_OnlineStatus': 'Hive Hub Status', 'Hive_OutsideTemperature': 'Outside Temperature', diff --git a/homeassistant/components/hive/switch.py b/homeassistant/components/hive/switch.py index fd4d3d69b50a06..ea4094d573cef3 100644 --- a/homeassistant/components/hive/switch.py +++ b/homeassistant/components/hive/switch.py @@ -3,8 +3,6 @@ from . import DATA_HIVE, DOMAIN -DEPENDENCIES = ['hive'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Hive switches.""" diff --git a/homeassistant/components/hlk_sw16/__init__.py b/homeassistant/components/hlk_sw16/__init__.py index acb604bc0103b3..79de0bd18be1ce 100644 --- a/homeassistant/components/hlk_sw16/__init__.py +++ b/homeassistant/components/hlk_sw16/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, async_dispatcher_connect) -REQUIREMENTS = ['hlk-sw16==0.0.7'] - _LOGGER = logging.getLogger(__name__) DATA_DEVICE_REGISTER = 'hlk_sw16_device_register' diff --git a/homeassistant/components/hlk_sw16/switch.py b/homeassistant/components/hlk_sw16/switch.py index 164a504fa34406..b7353f037c1269 100644 --- a/homeassistant/components/hlk_sw16/switch.py +++ b/homeassistant/components/hlk_sw16/switch.py @@ -4,9 +4,7 @@ from homeassistant.components.switch import ToggleEntity from homeassistant.const import CONF_NAME -from . import DATA_DEVICE_REGISTER, DOMAIN as HLK_SW16, SW16Device - -DEPENDENCIES = [HLK_SW16] +from . import DATA_DEVICE_REGISTER, SW16Device _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 9d7de58be4bc5a..f524455fedeaf6 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -26,8 +26,6 @@ from .util import ( show_setup_message, validate_entity_config, validate_media_player_features) -REQUIREMENTS = ['HAP-python==2.5.0'] - _LOGGER = logging.getLogger(__name__) MAX_DEVICES = 100 diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 2a43d0ac9ce5c4..6765db0085e8bf 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -12,8 +12,6 @@ ) from .const import DOMAIN # noqa: pylint: disable=unused-import -REQUIREMENTS = ['homekit[IP]==0.13.0'] - HOMEKIT_IGNORE = [ 'BSB002', 'Home Assistant Bridge', diff --git a/homeassistant/components/homekit_controller/alarm_control_panel.py b/homeassistant/components/homekit_controller/alarm_control_panel.py index f9bc25f4237e0c..fe15cfe2eab9a5 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -8,8 +8,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - ICON = 'mdi:security' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/binary_sensor.py b/homeassistant/components/homekit_controller/binary_sensor.py index 2bd03b18932146..a5b7008200236b 100644 --- a/homeassistant/components/homekit_controller/binary_sensor.py +++ b/homeassistant/components/homekit_controller/binary_sensor.py @@ -5,8 +5,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 67f1fb72bcfbb5..dfbd6f68daa53d 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -9,8 +9,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - _LOGGER = logging.getLogger(__name__) # Map of Homekit operation modes to hass modes diff --git a/homeassistant/components/homekit_controller/cover.py b/homeassistant/components/homekit_controller/cover.py index 26b7613ed2b6d5..0c9ce2bc5286b9 100644 --- a/homeassistant/components/homekit_controller/cover.py +++ b/homeassistant/components/homekit_controller/cover.py @@ -12,8 +12,6 @@ STATE_STOPPED = 'stopped' -DEPENDENCIES = ['homekit_controller'] - _LOGGER = logging.getLogger(__name__) CURRENT_GARAGE_STATE_MAP = { diff --git a/homeassistant/components/homekit_controller/light.py b/homeassistant/components/homekit_controller/light.py index cb9259df4a992d..a139b1f29328fa 100644 --- a/homeassistant/components/homekit_controller/light.py +++ b/homeassistant/components/homekit_controller/light.py @@ -7,8 +7,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homekit_controller/lock.py b/homeassistant/components/homekit_controller/lock.py index 0d0275fda164e7..67de2bfaf3f6b6 100644 --- a/homeassistant/components/homekit_controller/lock.py +++ b/homeassistant/components/homekit_controller/lock.py @@ -7,8 +7,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - _LOGGER = logging.getLogger(__name__) STATE_JAMMED = 'jammed' diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 8cbc8f248bafe0..b377da80142cf8 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -3,8 +3,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - HUMIDITY_ICON = 'mdi-water-percent' TEMP_C_ICON = "mdi-temperature-celsius" BRIGHTNESS_ICON = "mdi-brightness-6" diff --git a/homeassistant/components/homekit_controller/switch.py b/homeassistant/components/homekit_controller/switch.py index 34e83c06526758..c09502373a6089 100644 --- a/homeassistant/components/homekit_controller/switch.py +++ b/homeassistant/components/homekit_controller/switch.py @@ -5,8 +5,6 @@ from . import KNOWN_DEVICES, HomeKitEntity -DEPENDENCIES = ['homekit_controller'] - OUTLET_IN_USE = "outlet_in_use" _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematic/__init__.py b/homeassistant/components/homematic/__init__.py index a8109af5ed8f84..97c805aa2aca5d 100644 --- a/homeassistant/components/homematic/__init__.py +++ b/homeassistant/components/homematic/__init__.py @@ -14,8 +14,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyhomematic==0.1.58'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'homematic' diff --git a/homeassistant/components/homematic/binary_sensor.py b/homeassistant/components/homematic/binary_sensor.py index 7bf260a9bdc385..dfd7b7a72bd81b 100644 --- a/homeassistant/components/homematic/binary_sensor.py +++ b/homeassistant/components/homematic/binary_sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - SENSOR_TYPES_CLASS = { 'IPShutterContact': 'opening', 'MaxShutterContact': 'opening', diff --git a/homeassistant/components/homematic/climate.py b/homeassistant/components/homematic/climate.py index 146cad1bc4ca75..e10d486b727db9 100644 --- a/homeassistant/components/homematic/climate.py +++ b/homeassistant/components/homematic/climate.py @@ -9,8 +9,6 @@ from . import ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT, HMDevice -DEPENDENCIES = ['homematic'] - _LOGGER = logging.getLogger(__name__) STATE_BOOST = 'boost' diff --git a/homeassistant/components/homematic/cover.py b/homeassistant/components/homematic/cover.py index 33b764dc31fa21..387eb26f433d06 100644 --- a/homeassistant/components/homematic/cover.py +++ b/homeassistant/components/homematic/cover.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the platform.""" diff --git a/homeassistant/components/homematic/light.py b/homeassistant/components/homematic/light.py index c3601461173783..72aa9a977d4a72 100644 --- a/homeassistant/components/homematic/light.py +++ b/homeassistant/components/homematic/light.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - SUPPORT_HOMEMATIC = SUPPORT_BRIGHTNESS diff --git a/homeassistant/components/homematic/lock.py b/homeassistant/components/homematic/lock.py index 3c0ca040c5f522..7f796b32885cb8 100644 --- a/homeassistant/components/homematic/lock.py +++ b/homeassistant/components/homematic/lock.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Homematic lock platform.""" diff --git a/homeassistant/components/homematic/notify.py b/homeassistant/components/homematic/notify.py index 9054c1fa0ad6ca..74ea7095b41d3e 100644 --- a/homeassistant/components/homematic/notify.py +++ b/homeassistant/components/homematic/notify.py @@ -13,8 +13,6 @@ SERVICE_SET_DEVICE_VALUE) _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ["homematic"] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(ATTR_ADDRESS): vol.All(cv.string, vol.Upper), vol.Required(ATTR_CHANNEL): vol.Coerce(int), diff --git a/homeassistant/components/homematic/sensor.py b/homeassistant/components/homematic/sensor.py index 401d11f70c849b..fca8c746a49cc8 100644 --- a/homeassistant/components/homematic/sensor.py +++ b/homeassistant/components/homematic/sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - HM_STATE_HA_CAST = { 'RotaryHandleSensor': {0: 'closed', 1: 'tilted', 2: 'open'}, 'RotaryHandleSensorIP': {0: 'closed', 1: 'tilted', 2: 'open'}, diff --git a/homeassistant/components/homematic/switch.py b/homeassistant/components/homematic/switch.py index 393ad09b3104ef..b77b3a1f7008ba 100644 --- a/homeassistant/components/homematic/switch.py +++ b/homeassistant/components/homematic/switch.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematic'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the HomeMatic switch platform.""" diff --git a/homeassistant/components/homematicip_cloud/__init__.py b/homeassistant/components/homematicip_cloud/__init__.py index 1330a2750ae6db..4a24120be95592 100644 --- a/homeassistant/components/homematicip_cloud/__init__.py +++ b/homeassistant/components/homematicip_cloud/__init__.py @@ -15,8 +15,6 @@ from .device import HomematicipGenericDevice # noqa: F401 from .hap import HomematicipAuth, HomematicipHAP # noqa: F401 -REQUIREMENTS = ['homematicip==0.10.7'] - _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index df0201340ed127..cb35833c231a42 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematicip_cloud'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 44c17282dda03d..48e9520a952621 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -6,8 +6,6 @@ from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice from .device import ATTR_GROUP_MEMBER_UNREACHABLE -DEPENDENCIES = ['homematicip_cloud'] - _LOGGER = logging.getLogger(__name__) ATTR_MOTIONDETECTED = 'motion detected' diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 735e8789670452..e572e3d97546ad 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -5,8 +5,6 @@ from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice -DEPENDENCIES = ['homematicip_cloud'] - _LOGGER = logging.getLogger(__name__) HMIP_COVER_OPEN = 0 diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index f5bac66388c6d5..b67e4114db20d3 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -7,8 +7,6 @@ from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice -DEPENDENCIES = ['homematicip_cloud'] - _LOGGER = logging.getLogger(__name__) ATTR_ENERGY_COUNTER = 'energy_counter_kwh' diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 5f345f419fac09..201a5be6c51547 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['homematicip_cloud'] - ATTR_TEMPERATURE_OFFSET = 'temperature_offset' ATTR_WIND_DIRECTION = 'wind_direction' ATTR_WIND_DIRECTION_VARIATION = 'wind_direction_variation_in_degree' diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index f9713cd8c006cc..b96e0c4cf4d60c 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -6,8 +6,6 @@ from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice from .device import ATTR_GROUP_MEMBER_UNREACHABLE -DEPENDENCIES = ['homematicip_cloud'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index ba3157471f9c80..74b302b18fc33a 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -7,8 +7,6 @@ from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice -DEPENDENCIES = ['homematicip_cloud'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/homeworks/__init__.py b/homeassistant/components/homeworks/__init__.py index d0769ed25e6193..b722a5a4a2de29 100644 --- a/homeassistant/components/homeworks/__init__.py +++ b/homeassistant/components/homeworks/__init__.py @@ -12,8 +12,6 @@ async_dispatcher_connect, dispatcher_send) from homeassistant.util import slugify -REQUIREMENTS = ['pyhomeworks==0.0.6'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'homeworks' diff --git a/homeassistant/components/homeworks/light.py b/homeassistant/components/homeworks/light.py index ca41dff9834b44..710be7c0077ae2 100644 --- a/homeassistant/components/homeworks/light.py +++ b/homeassistant/components/homeworks/light.py @@ -11,8 +11,6 @@ CONF_ADDR, CONF_DIMMERS, CONF_RATE, ENTITY_SIGNAL, HOMEWORKS_CONTROLLER, HomeworksDevice) -DEPENDENCIES = ['homeworks'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 7460ed6e9d0a8c..df19f67a876d73 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -15,8 +15,6 @@ CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE, CONF_REGION) -REQUIREMENTS = ['evohomeclient==0.3.2', 'somecomfort==0.5.2'] - _LOGGER = logging.getLogger(__name__) ATTR_FAN = 'fan' diff --git a/homeassistant/components/horizon/media_player.py b/homeassistant/components/horizon/media_player.py index 51168e4ef2e1a7..ab72b051f1bd81 100644 --- a/homeassistant/components/horizon/media_player.py +++ b/homeassistant/components/horizon/media_player.py @@ -16,8 +16,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['horimote==0.4.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Horizon' diff --git a/homeassistant/components/hp_ilo/sensor.py b/homeassistant/components/hp_ilo/sensor.py index a017f0ee3e8c08..46fde885613929 100644 --- a/homeassistant/components/hp_ilo/sensor.py +++ b/homeassistant/components/hp_ilo/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['python-hpilo==3.9'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "HP ILO" diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py index fa7bf660b79f59..2fcaa266b8504c 100644 --- a/homeassistant/components/html5/notify.py +++ b/homeassistant/components/html5/notify.py @@ -24,10 +24,6 @@ ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, DOMAIN, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['pywebpush==1.9.2'] - -DEPENDENCIES = ['frontend'] - _LOGGER = logging.getLogger(__name__) REGISTRATIONS_FILE = 'html5_push_registrations.conf' diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 0bcf3f85ff7932..ad64b38200af52 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -29,8 +29,6 @@ from .static import CACHE_HEADERS, CachingStaticResource from .view import HomeAssistantView # noqa -REQUIREMENTS = ['aiohttp_cors==0.7.0'] - DOMAIN = 'http' CONF_API_PASSWORD = 'api_password' diff --git a/homeassistant/components/htu21d/sensor.py b/homeassistant/components/htu21d/sensor.py index 17182bb833d8c6..01c2b0399b9a76 100644 --- a/homeassistant/components/htu21d/sensor.py +++ b/homeassistant/components/htu21d/sensor.py @@ -12,9 +12,6 @@ from homeassistant.util import Throttle from homeassistant.util.temperature import celsius_to_fahrenheit -REQUIREMENTS = ['i2csense==0.0.4', - 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) CONF_I2C_BUS = 'i2c_bus' diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index a462b1b3072f18..8e401dfd2395e6 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -19,8 +19,6 @@ # https://github.com/quandyfactory/dicttoxml/issues/60 logging.getLogger('dicttoxml').setLevel(logging.WARNING) -REQUIREMENTS = ['huawei-lte-api==1.1.5'] - MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) DOMAIN = 'huawei_lte' diff --git a/homeassistant/components/huawei_lte/device_tracker.py b/homeassistant/components/huawei_lte/device_tracker.py index 69bf42fb3fe5a5..d6c49f5e255e16 100644 --- a/homeassistant/components/huawei_lte/device_tracker.py +++ b/homeassistant/components/huawei_lte/device_tracker.py @@ -11,8 +11,6 @@ from homeassistant.const import CONF_URL from ..huawei_lte import DATA_KEY, RouterData -DEPENDENCIES = ['huawei_lte'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_URL): cv.url, }) diff --git a/homeassistant/components/huawei_lte/notify.py b/homeassistant/components/huawei_lte/notify.py index 5e20a774c25d2a..6394140c07fae6 100644 --- a/homeassistant/components/huawei_lte/notify.py +++ b/homeassistant/components/huawei_lte/notify.py @@ -11,8 +11,6 @@ from ..huawei_lte import DATA_KEY -DEPENDENCIES = ['huawei_lte'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/huawei_lte/sensor.py b/homeassistant/components/huawei_lte/sensor.py index 42ad4b52f8d815..42bd1f16271403 100644 --- a/homeassistant/components/huawei_lte/sensor.py +++ b/homeassistant/components/huawei_lte/sensor.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['huawei_lte'] - DEFAULT_NAME_TEMPLATE = 'Huawei {} {}' DEFAULT_SENSORS = [ diff --git a/homeassistant/components/hue/__init__.py b/homeassistant/components/hue/__init__.py index 8f5c27f6516e8d..ac17e6e852f435 100644 --- a/homeassistant/components/hue/__init__.py +++ b/homeassistant/components/hue/__init__.py @@ -14,8 +14,6 @@ # Loading the config flow file will register the flow from .config_flow import configured_hosts -REQUIREMENTS = ['aiohue==1.9.1'] - _LOGGER = logging.getLogger(__name__) CONF_BRIDGES = "bridges" diff --git a/homeassistant/components/hue/light.py b/homeassistant/components/hue/light.py index 0725c86bd954e0..3ba92ef12a7cce 100644 --- a/homeassistant/components/hue/light.py +++ b/homeassistant/components/hue/light.py @@ -16,7 +16,6 @@ Light) from homeassistant.util import color -DEPENDENCIES = ['hue'] SCAN_INTERVAL = timedelta(seconds=5) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/hunterdouglas_powerview/scene.py b/homeassistant/components/hunterdouglas_powerview/scene.py index 7f0709aa6c1cd6..571e15ab94fe9f 100644 --- a/homeassistant/components/hunterdouglas_powerview/scene.py +++ b/homeassistant/components/hunterdouglas_powerview/scene.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import async_generate_entity_id _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['aiopvapi==1.6.14'] - ENTITY_ID_FORMAT = DOMAIN + '.{}' HUB_ADDRESS = 'address' diff --git a/homeassistant/components/hydrawise/__init__.py b/homeassistant/components/hydrawise/__init__.py index 9c7baf6db2e2dd..6ac0ee0322d37c 100644 --- a/homeassistant/components/hydrawise/__init__.py +++ b/homeassistant/components/hydrawise/__init__.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['hydrawiser==0.1.1'] - _LOGGER = logging.getLogger(__name__) ALLOWED_WATERING_TIME = [5, 10, 15, 30, 45, 60] diff --git a/homeassistant/components/hydrawise/binary_sensor.py b/homeassistant/components/hydrawise/binary_sensor.py index 85a51d3649eb55..980e495c7f9d1f 100644 --- a/homeassistant/components/hydrawise/binary_sensor.py +++ b/homeassistant/components/hydrawise/binary_sensor.py @@ -12,8 +12,6 @@ BINARY_SENSORS, DATA_HYDRAWISE, DEVICE_MAP, DEVICE_MAP_INDEX, HydrawiseEntity) -DEPENDENCIES = ['hydrawise'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index fc15a54ed60963..908529c783d276 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -10,8 +10,6 @@ from . import ( DATA_HYDRAWISE, DEVICE_MAP, DEVICE_MAP_INDEX, SENSORS, HydrawiseEntity) -DEPENDENCIES = ['hydrawise'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/hydrawise/switch.py b/homeassistant/components/hydrawise/switch.py index dcbd5274a6237d..ccfa9333e0037e 100644 --- a/homeassistant/components/hydrawise/switch.py +++ b/homeassistant/components/hydrawise/switch.py @@ -12,8 +12,6 @@ DEFAULT_WATERING_TIME, DEVICE_MAP, DEVICE_MAP_INDEX, SWITCHES, HydrawiseEntity) -DEPENDENCIES = ['hydrawise'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/hydroquebec/sensor.py b/homeassistant/components/hydroquebec/sensor.py index 5f0fd9e01aded9..0ec48f3058d817 100644 --- a/homeassistant/components/hydroquebec/sensor.py +++ b/homeassistant/components/hydroquebec/sensor.py @@ -20,8 +20,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyhydroquebec==2.2.2'] - _LOGGER = logging.getLogger(__name__) KILOWATT_HOUR = ENERGY_KILO_WATT_HOUR diff --git a/homeassistant/components/ialarm/alarm_control_panel.py b/homeassistant/components/ialarm/alarm_control_panel.py index 8152c2496e603c..27ff4fc6829f44 100644 --- a/homeassistant/components/ialarm/alarm_control_panel.py +++ b/homeassistant/components/ialarm/alarm_control_panel.py @@ -12,8 +12,6 @@ STATE_ALARM_TRIGGERED) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyialarm==0.3'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'iAlarm' diff --git a/homeassistant/components/icloud/device_tracker.py b/homeassistant/components/icloud/device_tracker.py index 1d0e0d2fafb0f6..908fe5ecf90fb4 100644 --- a/homeassistant/components/icloud/device_tracker.py +++ b/homeassistant/components/icloud/device_tracker.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyicloud==0.9.1'] - CONF_ACCOUNTNAME = 'account_name' CONF_MAX_INTERVAL = 'max_interval' CONF_GPS_ACCURACY_THRESHOLD = 'gps_accuracy_threshold' diff --git a/homeassistant/components/idteck_prox/__init__.py b/homeassistant/components/idteck_prox/__init__.py index 3de7aa7cc8c5fa..bfb227e0fc1014 100644 --- a/homeassistant/components/idteck_prox/__init__.py +++ b/homeassistant/components/idteck_prox/__init__.py @@ -7,8 +7,6 @@ from homeassistant.const import ( CONF_HOST, CONF_PORT, CONF_NAME, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['rfk101py==0.0.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "idteck_prox" diff --git a/homeassistant/components/ifttt/__init__.py b/homeassistant/components/ifttt/__init__.py index bad3984ea5bd20..6b5934702aaeb4 100644 --- a/homeassistant/components/ifttt/__init__.py +++ b/homeassistant/components/ifttt/__init__.py @@ -9,9 +9,6 @@ from homeassistant.helpers import config_entry_flow import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyfttt==0.3'] -DEPENDENCIES = ['webhook'] - _LOGGER = logging.getLogger(__name__) EVENT_RECEIVED = 'ifttt_webhook_received' diff --git a/homeassistant/components/ifttt/alarm_control_panel.py b/homeassistant/components/ifttt/alarm_control_panel.py index 3f806173196897..a0492a210e03e0 100644 --- a/homeassistant/components/ifttt/alarm_control_panel.py +++ b/homeassistant/components/ifttt/alarm_control_panel.py @@ -15,8 +15,6 @@ from . import ATTR_EVENT, DOMAIN as IFTTT_DOMAIN, SERVICE_TRIGGER -DEPENDENCIES = ['ifttt'] - _LOGGER = logging.getLogger(__name__) ALLOWED_STATES = [ diff --git a/homeassistant/components/iglo/light.py b/homeassistant/components/iglo/light.py index 6851141efb46be..1a6b5839029c9a 100644 --- a/homeassistant/components/iglo/light.py +++ b/homeassistant/components/iglo/light.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['iglo==1.2.7'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'iGlo Light' diff --git a/homeassistant/components/ihc/__init__.py b/homeassistant/components/ihc/__init__.py index 102acd82551c4f..7d8acfbdf2eacb 100644 --- a/homeassistant/components/ihc/__init__.py +++ b/homeassistant/components/ihc/__init__.py @@ -21,8 +21,6 @@ SERVICE_SET_RUNTIME_VALUE_FLOAT, SERVICE_SET_RUNTIME_VALUE_INT) from .util import async_pulse -REQUIREMENTS = ['ihcsdk==2.3.0', 'defusedxml==0.5.0'] - _LOGGER = logging.getLogger(__name__) AUTO_SETUP_YAML = 'ihc_auto_setup.yaml' diff --git a/homeassistant/components/ihc/binary_sensor.py b/homeassistant/components/ihc/binary_sensor.py index 69e3e1685af8c8..a9a2b66cdde1f6 100644 --- a/homeassistant/components/ihc/binary_sensor.py +++ b/homeassistant/components/ihc/binary_sensor.py @@ -6,8 +6,6 @@ from .const import CONF_INVERTING from .ihcdevice import IHCDevice -DEPENDENCIES = ['ihc'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the IHC binary sensor platform.""" diff --git a/homeassistant/components/ihc/light.py b/homeassistant/components/ihc/light.py index ad6d0fb6511859..72c0dc8f0ba946 100644 --- a/homeassistant/components/ihc/light.py +++ b/homeassistant/components/ihc/light.py @@ -9,8 +9,6 @@ from .ihcdevice import IHCDevice from .util import async_pulse, async_set_bool, async_set_int -DEPENDENCIES = ['ihc'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ihc/sensor.py b/homeassistant/components/ihc/sensor.py index fd1f2cee53a846..4c63cf41e965ff 100644 --- a/homeassistant/components/ihc/sensor.py +++ b/homeassistant/components/ihc/sensor.py @@ -5,8 +5,6 @@ from . import IHC_CONTROLLER, IHC_DATA, IHC_INFO from .ihcdevice import IHCDevice -DEPENDENCIES = ['ihc'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the IHC sensor platform.""" diff --git a/homeassistant/components/ihc/switch.py b/homeassistant/components/ihc/switch.py index e2189492b8f435..6d3a72a3b661cd 100644 --- a/homeassistant/components/ihc/switch.py +++ b/homeassistant/components/ihc/switch.py @@ -6,8 +6,6 @@ from .ihcdevice import IHCDevice from .util import async_pulse, async_set_bool -DEPENDENCIES = ['ihc'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the IHC switch platform.""" diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index e5193985629629..ce49ebf932ea83 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'image_processing' -DEPENDENCIES = ['camera'] - SCAN_INTERVAL = timedelta(seconds=10) DEVICE_CLASSES = [ diff --git a/homeassistant/components/imap/sensor.py b/homeassistant/components/imap/sensor.py index 5ff23eb8e5d5c4..cbc470beec8e1b 100644 --- a/homeassistant/components/imap/sensor.py +++ b/homeassistant/components/imap/sensor.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['aioimaplib==0.7.15'] - CONF_SERVER = 'server' CONF_FOLDER = 'folder' CONF_SEARCH = 'search' diff --git a/homeassistant/components/influxdb/__init__.py b/homeassistant/components/influxdb/__init__.py index 551996983c8004..bf2ba1b8ecc71c 100644 --- a/homeassistant/components/influxdb/__init__.py +++ b/homeassistant/components/influxdb/__init__.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_values import EntityValues -REQUIREMENTS = ['influxdb==5.2.0'] - _LOGGER = logging.getLogger(__name__) CONF_DB_NAME = 'database' diff --git a/homeassistant/components/influxdb/sensor.py b/homeassistant/components/influxdb/sensor.py index 3bec7e3c657780..81a93cfc51dfdb 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -18,8 +18,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['influxdb==5.2.0'] - DEFAULT_HOST = 'localhost' DEFAULT_PORT = 8086 DEFAULT_DATABASE = 'home_assistant' diff --git a/homeassistant/components/insteon/__init__.py b/homeassistant/components/insteon/__init__.py index a462ac0f63efe3..a1eea2fb1dfb90 100644 --- a/homeassistant/components/insteon/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -13,8 +13,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['insteonplm==0.15.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'insteon' diff --git a/homeassistant/components/insteon/binary_sensor.py b/homeassistant/components/insteon/binary_sensor.py index 6f1e56756394f0..50e7a8fb6461bf 100644 --- a/homeassistant/components/insteon/binary_sensor.py +++ b/homeassistant/components/insteon/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['insteon'] - SENSOR_TYPES = { 'openClosedSensor': 'opening', 'ioLincSensor': 'opening', diff --git a/homeassistant/components/insteon/cover.py b/homeassistant/components/insteon/cover.py index 1bb316152a9db9..da339bb4b65a09 100644 --- a/homeassistant/components/insteon/cover.py +++ b/homeassistant/components/insteon/cover.py @@ -10,7 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['insteon'] SUPPORTED_FEATURES = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION diff --git a/homeassistant/components/insteon/fan.py b/homeassistant/components/insteon/fan.py index 26a56d6df98b3b..888fcfe959a9df 100644 --- a/homeassistant/components/insteon/fan.py +++ b/homeassistant/components/insteon/fan.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['insteon'] - SPEED_TO_HEX = { SPEED_OFF: 0x00, SPEED_LOW: 0x3f, diff --git a/homeassistant/components/insteon/light.py b/homeassistant/components/insteon/light.py index 676c053325c65f..5103bedc6b6587 100644 --- a/homeassistant/components/insteon/light.py +++ b/homeassistant/components/insteon/light.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['insteon'] - MAX_BRIGHTNESS = 255 diff --git a/homeassistant/components/insteon/sensor.py b/homeassistant/components/insteon/sensor.py index edea87e1f738aa..a7c1c0b89efbfb 100644 --- a/homeassistant/components/insteon/sensor.py +++ b/homeassistant/components/insteon/sensor.py @@ -5,8 +5,6 @@ from . import InsteonEntity -DEPENDENCIES = ['insteon'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/insteon/switch.py b/homeassistant/components/insteon/switch.py index 4fdcdb20bb2847..6c7b2b02031ced 100644 --- a/homeassistant/components/insteon/switch.py +++ b/homeassistant/components/insteon/switch.py @@ -5,8 +5,6 @@ from . import InsteonEntity -DEPENDENCIES = ['insteon'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ios/__init__.py b/homeassistant/components/ios/__init__.py index cc8bd62293a18b..a9395ed5f5d93f 100644 --- a/homeassistant/components/ios/__init__.py +++ b/homeassistant/components/ios/__init__.py @@ -17,8 +17,6 @@ DOMAIN = 'ios' -DEPENDENCIES = ['device_tracker', 'http', 'zeroconf'] - CONF_PUSH = 'push' CONF_PUSH_CATEGORIES = 'categories' CONF_PUSH_CATEGORIES_NAME = 'name' diff --git a/homeassistant/components/ios/notify.py b/homeassistant/components/ios/notify.py index 1f8aade4ec17a8..ecbbfb2056c26c 100644 --- a/homeassistant/components/ios/notify.py +++ b/homeassistant/components/ios/notify.py @@ -14,8 +14,6 @@ PUSH_URL = "https://ios-push.home-assistant.io/push" -DEPENDENCIES = ["ios"] - # pylint: disable=invalid-name def log_rate_limits(hass, target, resp, level=20): diff --git a/homeassistant/components/ios/sensor.py b/homeassistant/components/ios/sensor.py index 404b313368cebe..5c5be2b262666a 100644 --- a/homeassistant/components/ios/sensor.py +++ b/homeassistant/components/ios/sensor.py @@ -3,8 +3,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.icon import icon_for_battery_level -DEPENDENCIES = ['ios'] - SENSOR_TYPES = { 'level': ['Battery Level', '%'], 'state': ['Battery State', None] diff --git a/homeassistant/components/iota/__init__.py b/homeassistant/components/iota/__init__.py index e28de61aad0174..c3140e00b97aae 100644 --- a/homeassistant/components/iota/__init__.py +++ b/homeassistant/components/iota/__init__.py @@ -8,8 +8,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyota==2.0.5'] - _LOGGER = logging.getLogger(__name__) CONF_IRI = 'iri' diff --git a/homeassistant/components/iota/sensor.py b/homeassistant/components/iota/sensor.py index 2955828aff5a77..c278ab7288d06f 100644 --- a/homeassistant/components/iota/sensor.py +++ b/homeassistant/components/iota/sensor.py @@ -15,8 +15,6 @@ CONF_SEED = 'seed' CONF_TESTNET = 'testnet' -DEPENDENCIES = ['iota'] - SCAN_INTERVAL = timedelta(minutes=3) diff --git a/homeassistant/components/iperf3/__init__.py b/homeassistant/components/iperf3/__init__.py index 01ac2194f355e7..00a5738dbd61d8 100644 --- a/homeassistant/components/iperf3/__init__.py +++ b/homeassistant/components/iperf3/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['iperf3==0.1.10'] - DOMAIN = 'iperf3' DATA_UPDATED = '{}_data_updated'.format(DOMAIN) diff --git a/homeassistant/components/iperf3/sensor.py b/homeassistant/components/iperf3/sensor.py index db9aafcdf4bb19..efc34d8bdef000 100644 --- a/homeassistant/components/iperf3/sensor.py +++ b/homeassistant/components/iperf3/sensor.py @@ -6,8 +6,6 @@ from . import ATTR_VERSION, DATA_UPDATED, DOMAIN as IPERF3_DOMAIN, SENSOR_TYPES -DEPENDENCIES = ['iperf3'] - ATTRIBUTION = 'Data retrieved using Iperf3' ICON = 'mdi:speedometer' diff --git a/homeassistant/components/ipma/weather.py b/homeassistant/components/ipma/weather.py index 7122957ad12d95..e976bcb9896a51 100644 --- a/homeassistant/components/ipma/weather.py +++ b/homeassistant/components/ipma/weather.py @@ -15,8 +15,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['pyipma==1.2.1'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Instituto Português do Mar e Atmosfera' diff --git a/homeassistant/components/irish_rail_transport/sensor.py b/homeassistant/components/irish_rail_transport/sensor.py index e17ecfde59da5b..586568ca9eff44 100644 --- a/homeassistant/components/irish_rail_transport/sensor.py +++ b/homeassistant/components/irish_rail_transport/sensor.py @@ -9,8 +9,6 @@ from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyirishrail==0.0.2'] - _LOGGER = logging.getLogger(__name__) ATTR_STATION = "Station" diff --git a/homeassistant/components/islamic_prayer_times/sensor.py b/homeassistant/components/islamic_prayer_times/sensor.py index 9efbc237e30a5e..c50c01c2eceec5 100644 --- a/homeassistant/components/islamic_prayer_times/sensor.py +++ b/homeassistant/components/islamic_prayer_times/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_point_in_time -REQUIREMENTS = ['prayer_times_calculator==0.0.3'] - _LOGGER = logging.getLogger(__name__) PRAYER_TIMES_ICON = 'mdi:calendar-clock' diff --git a/homeassistant/components/iss/binary_sensor.py b/homeassistant/components/iss/binary_sensor.py index 381bc16791826e..97e5087819ee3c 100644 --- a/homeassistant/components/iss/binary_sensor.py +++ b/homeassistant/components/iss/binary_sensor.py @@ -12,8 +12,6 @@ CONF_NAME, ATTR_LONGITUDE, ATTR_LATITUDE, CONF_SHOW_ON_MAP) from homeassistant.util import Throttle -REQUIREMENTS = ['pyiss==1.0.1'] - _LOGGER = logging.getLogger(__name__) ATTR_ISS_NEXT_RISE = 'next_rise' diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index 4eaa71deececa5..de5e09f6238726 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ConfigType, Dict -REQUIREMENTS = ['PyISY==1.1.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'isy994' diff --git a/homeassistant/components/itach/remote.py b/homeassistant/components/itach/remote.py index beb773838fb17d..54dfa1fcfb92f8 100644 --- a/homeassistant/components/itach/remote.py +++ b/homeassistant/components/itach/remote.py @@ -10,8 +10,6 @@ CONF_DEVICES) from homeassistant.components.remote import PLATFORM_SCHEMA -REQUIREMENTS = ['pyitachip2ir==0.0.7'] - _LOGGER = logging.getLogger(__name__) DEFAULT_PORT = 4998 diff --git a/homeassistant/components/jewish_calendar/sensor.py b/homeassistant/components/jewish_calendar/sensor.py index 478bbed98fa6c7..ec86abecc4413d 100644 --- a/homeassistant/components/jewish_calendar/sensor.py +++ b/homeassistant/components/jewish_calendar/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.sun import get_astral_event_date import homeassistant.util.dt as dt_util -REQUIREMENTS = ['hdate==0.8.7'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { diff --git a/homeassistant/components/joaoapps_join/__init__.py b/homeassistant/components/joaoapps_join/__init__.py index f1371deed2bdf5..4a3cf737c96ff9 100644 --- a/homeassistant/components/joaoapps_join/__init__.py +++ b/homeassistant/components/joaoapps_join/__init__.py @@ -6,8 +6,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import CONF_NAME, CONF_API_KEY -REQUIREMENTS = ['python-join-api==0.0.4'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'joaoapps_join' diff --git a/homeassistant/components/joaoapps_join/notify.py b/homeassistant/components/joaoapps_join/notify.py index 0137520049d423..d9eabce5476f5d 100644 --- a/homeassistant/components/joaoapps_join/notify.py +++ b/homeassistant/components/joaoapps_join/notify.py @@ -7,8 +7,6 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-join-api==0.0.4'] - _LOGGER = logging.getLogger(__name__) CONF_DEVICE_ID = 'device_id' diff --git a/homeassistant/components/juicenet/__init__.py b/homeassistant/components/juicenet/__init__.py index f62331d1502ea0..919322487b161c 100644 --- a/homeassistant/components/juicenet/__init__.py +++ b/homeassistant/components/juicenet/__init__.py @@ -8,8 +8,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-juicenet==0.0.5'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'juicenet' diff --git a/homeassistant/components/juicenet/sensor.py b/homeassistant/components/juicenet/sensor.py index 6b55e539547d24..60369b1f92a732 100644 --- a/homeassistant/components/juicenet/sensor.py +++ b/homeassistant/components/juicenet/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['juicenet'] - SENSOR_TYPES = { 'status': ['Charging Status', None], 'temperature': ['Temperature', TEMP_CELSIUS], diff --git a/homeassistant/components/keenetic_ndms2/device_tracker.py b/homeassistant/components/keenetic_ndms2/device_tracker.py index f873507112dc62..e52dff7476dfab 100644 --- a/homeassistant/components/keenetic_ndms2/device_tracker.py +++ b/homeassistant/components/keenetic_ndms2/device_tracker.py @@ -10,8 +10,6 @@ CONF_HOST, CONF_PORT, CONF_PASSWORD, CONF_USERNAME ) -REQUIREMENTS = ['ndms2_client==0.0.6'] - _LOGGER = logging.getLogger(__name__) # Interface name to track devices for. Most likely one will not need to diff --git a/homeassistant/components/keyboard/__init__.py b/homeassistant/components/keyboard/__init__.py index 44accca2f56a7e..f841e7e9681974 100644 --- a/homeassistant/components/keyboard/__init__.py +++ b/homeassistant/components/keyboard/__init__.py @@ -6,8 +6,6 @@ SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_VOLUME_UP) -REQUIREMENTS = ['pyuserinput==0.1.11'] - DOMAIN = 'keyboard' TAP_KEY_SCHEMA = vol.Schema({}) diff --git a/homeassistant/components/keyboard_remote/__init__.py b/homeassistant/components/keyboard_remote/__init__.py index e786fe458a8460..71df70f51f0a96 100644 --- a/homeassistant/components/keyboard_remote/__init__.py +++ b/homeassistant/components/keyboard_remote/__init__.py @@ -11,8 +11,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['evdev==0.6.1'] - _LOGGER = logging.getLogger(__name__) DEVICE_DESCRIPTOR = 'device_descriptor' diff --git a/homeassistant/components/kira/__init__.py b/homeassistant/components/kira/__init__.py index d60d8e0cfeb10a..7cf27d342f51c9 100644 --- a/homeassistant/components/kira/__init__.py +++ b/homeassistant/components/kira/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pykira==0.1.1'] - DOMAIN = 'kira' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/kiwi/lock.py b/homeassistant/components/kiwi/lock.py index 0b5806425d95a7..bbeb2dce04a772 100644 --- a/homeassistant/components/kiwi/lock.py +++ b/homeassistant/components/kiwi/lock.py @@ -11,8 +11,6 @@ from homeassistant.helpers.event import async_call_later from homeassistant.core import callback -REQUIREMENTS = ['kiwiki-client==0.1.1'] - _LOGGER = logging.getLogger(__name__) ATTR_TYPE = 'hardware_type' diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index ea5b18b7ede91a..04b51730be1ca4 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.script import Script -REQUIREMENTS = ['xknx==0.10.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "knx" diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index 8ee21e24c5e978..65d10722500fc7 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -22,8 +22,6 @@ CONF__ACTION = 'turn_off_action' DEFAULT_NAME = 'KNX Binary Sensor' -DEPENDENCIES = ['knx'] - AUTOMATION_SCHEMA = vol.Schema({ vol.Optional(CONF_HOOK, default=CONF_DEFAULT_HOOK): cv.string, vol.Optional(CONF_COUNTER, default=CONF_DEFAULT_COUNTER): cv.port, diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index e11e5449326bf5..f4835389dfa043 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -39,8 +39,6 @@ DEFAULT_SETPOINT_SHIFT_STEP = 0.5 DEFAULT_SETPOINT_SHIFT_MAX = 6 DEFAULT_SETPOINT_SHIFT_MIN = -6 -DEPENDENCIES = ['knx'] - # Map KNX operation modes to HA modes. This list might not be full. OPERATION_MODES = { # Map DPT 201.100 HVAC operating modes diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index b2b287d1e87d23..bbee54e00cd95f 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -25,8 +25,6 @@ DEFAULT_TRAVEL_TIME = 25 DEFAULT_NAME = 'KNX Cover' -DEPENDENCIES = ['knx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_MOVE_LONG_ADDRESS): cv.string, diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index cf59f1fc135b3b..b94d91514af40e 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -30,7 +30,6 @@ DEFAULT_COLOR_TEMP_MODE = 'absolute' DEFAULT_MIN_KELVIN = 2700 # 370 mireds DEFAULT_MAX_KELVIN = 6000 # 166 mireds -DEPENDENCIES = ['knx'] class ColorTempModes(Enum): diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index 742252d187426f..486908c3cffd90 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -11,8 +11,6 @@ DEFAULT_NAME = 'KNX Notify' -DEPENDENCIES = ['knx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADDRESS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string diff --git a/homeassistant/components/knx/scene.py b/homeassistant/components/knx/scene.py index 4bf186c28ff7d4..4f0c7b2d4fcc4d 100644 --- a/homeassistant/components/knx/scene.py +++ b/homeassistant/components/knx/scene.py @@ -11,8 +11,6 @@ CONF_SCENE_NUMBER = 'scene_number' DEFAULT_NAME = 'KNX SCENE' -DEPENDENCIES = ['knx'] - PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): 'knx', vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index 7ddafe53be44b0..bb3128eaee7821 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -10,8 +10,6 @@ from . import ATTR_DISCOVER_DEVICES, DATA_KNX DEFAULT_NAME = 'KNX Sensor' -DEPENDENCIES = ['knx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADDRESS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/knx/switch.py b/homeassistant/components/knx/switch.py index e3beff39677954..461b27e94c0d54 100644 --- a/homeassistant/components/knx/switch.py +++ b/homeassistant/components/knx/switch.py @@ -11,8 +11,6 @@ CONF_STATE_ADDRESS = 'state_address' DEFAULT_NAME = 'KNX Switch' -DEPENDENCIES = ['knx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_ADDRESS): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index 81c93dba2ac417..96fb02a08a0395 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -31,8 +31,6 @@ from homeassistant.util.yaml import dump import homeassistant.util.dt as dt_util -REQUIREMENTS = ['jsonrpc-async==0.6', 'jsonrpc-websocket==0.6'] - _LOGGER = logging.getLogger(__name__) EVENT_KODI_CALL_METHOD_RESULT = 'kodi_call_method_result' diff --git a/homeassistant/components/kodi/notify.py b/homeassistant/components/kodi/notify.py index f6ee2c47b96b7c..fb5326c83c8fd4 100644 --- a/homeassistant/components/kodi/notify.py +++ b/homeassistant/components/kodi/notify.py @@ -14,8 +14,6 @@ ATTR_DATA, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['jsonrpc-async==0.6'] - _LOGGER = logging.getLogger(__name__) DEFAULT_PORT = 8080 diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index 276e395817c0d5..ee4ba16e54c33e 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -32,8 +32,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['konnected==0.1.5'] - _BINARY_SENSOR_SCHEMA = vol.All( vol.Schema({ vol.Exclusive(CONF_PIN, 's_pin'): vol.Any(*PIN_TO_ZONE), @@ -96,8 +94,6 @@ extra=vol.ALLOW_EXTRA, ) -DEPENDENCIES = ['http'] - async def async_setup(hass, config): """Set up the Konnected platform.""" diff --git a/homeassistant/components/konnected/binary_sensor.py b/homeassistant/components/konnected/binary_sensor.py index 1fbfbea1861c25..3abd9be6c4b79b 100644 --- a/homeassistant/components/konnected/binary_sensor.py +++ b/homeassistant/components/konnected/binary_sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['konnected'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/konnected/sensor.py b/homeassistant/components/konnected/sensor.py index a48d1a58619600..7881eacff40f5b 100644 --- a/homeassistant/components/konnected/sensor.py +++ b/homeassistant/components/konnected/sensor.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['konnected'] - SENSOR_TYPES = { DEVICE_CLASS_TEMPERATURE: ['Temperature', TEMP_CELSIUS], DEVICE_CLASS_HUMIDITY: ['Humidity', '%'] diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index 3db602215b968d..841e84e2487edd 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['konnected'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/kwb/sensor.py b/homeassistant/components/kwb/sensor.py index bad0ea3cdede51..7a153970d189c2 100644 --- a/homeassistant/components/kwb/sensor.py +++ b/homeassistant/components/kwb/sensor.py @@ -9,8 +9,6 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pykwb==0.0.8'] - _LOGGER = logging.getLogger(__name__) DEFAULT_RAW = False diff --git a/homeassistant/components/lacrosse/sensor.py b/homeassistant/components/lacrosse/sensor.py index 9240343a5e3e69..dea51b0c9173e9 100644 --- a/homeassistant/components/lacrosse/sensor.py +++ b/homeassistant/components/lacrosse/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util import dt as dt_util -REQUIREMENTS = ['pylacrosse==0.3.1'] - _LOGGER = logging.getLogger(__name__) CONF_BAUD = 'baud' diff --git a/homeassistant/components/lametric/__init__.py b/homeassistant/components/lametric/__init__.py index 0c3c8b08dd732e..057594f42aed75 100644 --- a/homeassistant/components/lametric/__init__.py +++ b/homeassistant/components/lametric/__init__.py @@ -5,8 +5,6 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['lmnotify==0.0.4'] - _LOGGER = logging.getLogger(__name__) CONF_CLIENT_ID = 'client_id' diff --git a/homeassistant/components/lametric/notify.py b/homeassistant/components/lametric/notify.py index 358bb056b00e33..92b254cd2b01d2 100644 --- a/homeassistant/components/lametric/notify.py +++ b/homeassistant/components/lametric/notify.py @@ -11,8 +11,6 @@ from . import DOMAIN as LAMETRIC_DOMAIN -REQUIREMENTS = ['lmnotify==0.0.4'] - _LOGGER = logging.getLogger(__name__) AVAILABLE_PRIORITIES = ['info', 'warning', 'critical'] @@ -21,8 +19,6 @@ CONF_LIFETIME = 'lifetime' CONF_PRIORITY = 'priority' -DEPENDENCIES = ['lametric'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_ICON, default='a7956'): cv.string, vol.Optional(CONF_LIFETIME, default=10): cv.positive_int, diff --git a/homeassistant/components/lastfm/sensor.py b/homeassistant/components/lastfm/sensor.py index e4e28eff4f1819..32774b1bf284bd 100644 --- a/homeassistant/components/lastfm/sensor.py +++ b/homeassistant/components/lastfm/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pylast==3.1.0'] - _LOGGER = logging.getLogger(__name__) ATTR_LAST_PLAYED = 'last_played' diff --git a/homeassistant/components/launch_library/sensor.py b/homeassistant/components/launch_library/sensor.py index 4b42ddba268b0b..a1c5b5825a9b39 100644 --- a/homeassistant/components/launch_library/sensor.py +++ b/homeassistant/components/launch_library/sensor.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pylaunches==0.2.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Launch Library." diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index 44f69c261b9c88..418b6ffa89df0c 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -20,8 +20,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pypck==0.5.9'] - def has_unique_connection_names(connections): """Validate that all connection names are unique. diff --git a/homeassistant/components/lcn/binary_sensor.py b/homeassistant/components/lcn/binary_sensor.py index 0ffa2e50d8b21d..ec37d3e5128ffc 100755 --- a/homeassistant/components/lcn/binary_sensor.py +++ b/homeassistant/components/lcn/binary_sensor.py @@ -6,8 +6,6 @@ from .const import ( BINSENSOR_PORTS, CONF_CONNECTIONS, CONF_SOURCE, DATA_LCN, SETPOINTS) -DEPENDENCIES = ['lcn'] - async def async_setup_platform(hass, hass_config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lcn/cover.py b/homeassistant/components/lcn/cover.py index a32ff7c23f4605..7123f2d5d0a579 100755 --- a/homeassistant/components/lcn/cover.py +++ b/homeassistant/components/lcn/cover.py @@ -5,8 +5,6 @@ from . import LcnDevice, get_connection from .const import CONF_CONNECTIONS, CONF_MOTOR, DATA_LCN -DEPENDENCIES = ['lcn'] - async def async_setup_platform(hass, hass_config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lcn/light.py b/homeassistant/components/lcn/light.py index 00b78259354e51..653873ba78a373 100644 --- a/homeassistant/components/lcn/light.py +++ b/homeassistant/components/lcn/light.py @@ -9,8 +9,6 @@ CONF_CONNECTIONS, CONF_DIMMABLE, CONF_OUTPUT, CONF_TRANSITION, DATA_LCN, OUTPUT_PORTS) -DEPENDENCIES = ['lcn'] - async def async_setup_platform( hass, hass_config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py index 5e50d092ada05d..48ac8c7266c40d 100755 --- a/homeassistant/components/lcn/sensor.py +++ b/homeassistant/components/lcn/sensor.py @@ -6,8 +6,6 @@ CONF_CONNECTIONS, CONF_SOURCE, DATA_LCN, LED_PORTS, S0_INPUTS, SETPOINTS, THRESHOLDS, VARIABLES) -DEPENDENCIES = ['lcn'] - async def async_setup_platform(hass, hass_config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lcn/switch.py b/homeassistant/components/lcn/switch.py index 7c375f4a598c79..48ae579fbcd7c3 100755 --- a/homeassistant/components/lcn/switch.py +++ b/homeassistant/components/lcn/switch.py @@ -5,8 +5,6 @@ from . import LcnDevice, get_connection from .const import CONF_CONNECTIONS, CONF_OUTPUT, DATA_LCN, OUTPUT_PORTS -DEPENDENCIES = ['lcn'] - async def async_setup_platform(hass, hass_config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lg_netcast/media_player.py b/homeassistant/components/lg_netcast/media_player.py index 12fee5fae96eda..da5946de1ef45f 100644 --- a/homeassistant/components/lg_netcast/media_player.py +++ b/homeassistant/components/lg_netcast/media_player.py @@ -17,8 +17,6 @@ STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pylgnetcast-homeassistant==0.2.0.dev0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'LG TV Remote' diff --git a/homeassistant/components/lg_soundbar/media_player.py b/homeassistant/components/lg_soundbar/media_player.py index 2e2481a462b5a9..938b4e437c16c0 100644 --- a/homeassistant/components/lg_soundbar/media_player.py +++ b/homeassistant/components/lg_soundbar/media_player.py @@ -9,8 +9,6 @@ from homeassistant.const import STATE_ON -REQUIREMENTS = ['temescal==0.1'] - _LOGGER = logging.getLogger(__name__) SUPPORT_LG = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | SUPPORT_SELECT_SOURCE \ diff --git a/homeassistant/components/lifx/__init__.py b/homeassistant/components/lifx/__init__.py index 82802bab4af75c..849fecad487db6 100644 --- a/homeassistant/components/lifx/__init__.py +++ b/homeassistant/components/lifx/__init__.py @@ -8,8 +8,6 @@ from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN DOMAIN = 'lifx' -REQUIREMENTS = ['aiolifx==0.6.7'] - CONF_SERVER = 'server' CONF_BROADCAST = 'broadcast' diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 014ca9ae6c80b5..04f756e6dede3f 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -30,9 +30,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lifx'] -REQUIREMENTS = ['aiolifx_effects==0.2.1'] - SCAN_INTERVAL = timedelta(seconds=10) DISCOVERY_INTERVAL = 60 diff --git a/homeassistant/components/lifx_legacy/light.py b/homeassistant/components/lifx_legacy/light.py index 6c5f68937f88a5..a31b875f21e4e3 100644 --- a/homeassistant/components/lifx_legacy/light.py +++ b/homeassistant/components/lifx_legacy/light.py @@ -22,8 +22,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['liffylights==0.9.4'] - BYTE_MAX = 255 CONF_BROADCAST = 'broadcast' diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index db2e9ce0197b50..f9ce6eb05d4158 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -24,7 +24,6 @@ import homeassistant.util.color as color_util DOMAIN = 'light' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_LIGHTS = 'all lights' diff --git a/homeassistant/components/lightwave/__init__.py b/homeassistant/components/lightwave/__init__.py index f6e11352265dcf..2337c582b2d083 100644 --- a/homeassistant/components/lightwave/__init__.py +++ b/homeassistant/components/lightwave/__init__.py @@ -5,8 +5,6 @@ CONF_SWITCHES) from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['lightwave==0.15'] - LIGHTWAVE_LINK = 'lightwave_link' DOMAIN = 'lightwave' diff --git a/homeassistant/components/lightwave/light.py b/homeassistant/components/lightwave/light.py index f22533d2548dca..68c94300317e73 100644 --- a/homeassistant/components/lightwave/light.py +++ b/homeassistant/components/lightwave/light.py @@ -5,8 +5,6 @@ from . import LIGHTWAVE_LINK -DEPENDENCIES = ['lightwave'] - MAX_BRIGHTNESS = 255 diff --git a/homeassistant/components/lightwave/switch.py b/homeassistant/components/lightwave/switch.py index dfa93b4b151844..0d7e2cd382558a 100644 --- a/homeassistant/components/lightwave/switch.py +++ b/homeassistant/components/lightwave/switch.py @@ -4,8 +4,6 @@ from . import LIGHTWAVE_LINK -DEPENDENCIES = ['lightwave'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/limitlessled/light.py b/homeassistant/components/limitlessled/light.py index 4f187afa1d75df..fa12bc76de5355 100644 --- a/homeassistant/components/limitlessled/light.py +++ b/homeassistant/components/limitlessled/light.py @@ -15,8 +15,6 @@ color_temperature_mired_to_kelvin, color_hs_to_RGB) from homeassistant.helpers.restore_state import RestoreEntity -REQUIREMENTS = ['limitlessled==1.1.3'] - _LOGGER = logging.getLogger(__name__) CONF_BRIDGES = 'bridges' diff --git a/homeassistant/components/linksys_ap/device_tracker.py b/homeassistant/components/linksys_ap/device_tracker.py index 46cc78d4e4ae36..3871d5beda9a1a 100644 --- a/homeassistant/components/linksys_ap/device_tracker.py +++ b/homeassistant/components/linksys_ap/device_tracker.py @@ -14,8 +14,6 @@ INTERFACES = 2 DEFAULT_TIMEOUT = 10 -REQUIREMENTS = ['beautifulsoup4==4.7.1'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/linky/sensor.py b/homeassistant/components/linky/sensor.py index 35f85f15ed6332..63f7aaf5423382 100644 --- a/homeassistant/components/linky/sensor.py +++ b/homeassistant/components/linky/sensor.py @@ -12,7 +12,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pylinky==0.3.3'] _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(minutes=10) diff --git a/homeassistant/components/linode/__init__.py b/homeassistant/components/linode/__init__.py index 8bbd98c0acf778..f9270d95c078a7 100644 --- a/homeassistant/components/linode/__init__.py +++ b/homeassistant/components/linode/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['linode-api==4.1.9b1'] - _LOGGER = logging.getLogger(__name__) ATTR_CREATED = 'created' diff --git a/homeassistant/components/linode/binary_sensor.py b/homeassistant/components/linode/binary_sensor.py index 19455917dbb28b..69079b3e63ab8b 100644 --- a/homeassistant/components/linode/binary_sensor.py +++ b/homeassistant/components/linode/binary_sensor.py @@ -16,8 +16,6 @@ DEFAULT_NAME = 'Node' DEFAULT_DEVICE_CLASS = 'moving' -DEPENDENCIES = ['linode'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_NODES): vol.All(cv.ensure_list, [cv.string]), }) diff --git a/homeassistant/components/linode/switch.py b/homeassistant/components/linode/switch.py index e5f97ef756e403..6787d84937f67a 100644 --- a/homeassistant/components/linode/switch.py +++ b/homeassistant/components/linode/switch.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['linode'] - DEFAULT_NAME = 'Node' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/linux_battery/sensor.py b/homeassistant/components/linux_battery/sensor.py index 7164315de8ea42..87061174d2d270 100644 --- a/homeassistant/components/linux_battery/sensor.py +++ b/homeassistant/components/linux_battery/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['batinfo==0.4.2'] - _LOGGER = logging.getLogger(__name__) ATTR_PATH = 'path' diff --git a/homeassistant/components/lirc/__init__.py b/homeassistant/components/lirc/__init__.py index 0f00eda20072c0..c3077cf6f44401 100644 --- a/homeassistant/components/lirc/__init__.py +++ b/homeassistant/components/lirc/__init__.py @@ -9,8 +9,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START) -REQUIREMENTS = ['python-lirc==1.2.3'] - _LOGGER = logging.getLogger(__name__) BUTTON_NAME = 'button_name' diff --git a/homeassistant/components/litejet/__init__.py b/homeassistant/components/litejet/__init__.py index b4e8e45fa0b742..d8e02b51870881 100644 --- a/homeassistant/components/litejet/__init__.py +++ b/homeassistant/components/litejet/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers import discovery from homeassistant.const import CONF_PORT -REQUIREMENTS = ['pylitejet==0.1'] - _LOGGER = logging.getLogger(__name__) CONF_EXCLUDE_NAMES = 'exclude_names' diff --git a/homeassistant/components/litejet/light.py b/homeassistant/components/litejet/light.py index e52e50ed21a858..b87d77ebe7c086 100644 --- a/homeassistant/components/litejet/light.py +++ b/homeassistant/components/litejet/light.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['litejet'] - ATTR_NUMBER = 'number' diff --git a/homeassistant/components/litejet/scene.py b/homeassistant/components/litejet/scene.py index 2563c9ceb0c480..c347140a6bdf6e 100644 --- a/homeassistant/components/litejet/scene.py +++ b/homeassistant/components/litejet/scene.py @@ -4,8 +4,6 @@ from homeassistant.components import litejet from homeassistant.components.scene import Scene -DEPENDENCIES = ['litejet'] - ATTR_NUMBER = 'number' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/litejet/switch.py b/homeassistant/components/litejet/switch.py index 9972dcb9f44797..7e3059dacd66fd 100644 --- a/homeassistant/components/litejet/switch.py +++ b/homeassistant/components/litejet/switch.py @@ -4,8 +4,6 @@ from homeassistant.components import litejet from homeassistant.components.switch import SwitchDevice -DEPENDENCIES = ['litejet'] - ATTR_NUMBER = 'number' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/liveboxplaytv/media_player.py b/homeassistant/components/liveboxplaytv/media_player.py index 1ee9931d233c0d..05ceb68cc94e3c 100644 --- a/homeassistant/components/liveboxplaytv/media_player.py +++ b/homeassistant/components/liveboxplaytv/media_player.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['liveboxplaytv==2.0.2', 'pyteleloisirs==3.4'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Livebox Play TV' diff --git a/homeassistant/components/locative/__init__.py b/homeassistant/components/locative/__init__.py index 335ae4cfe1eae9..f21c55af28ad4a 100644 --- a/homeassistant/components/locative/__init__.py +++ b/homeassistant/components/locative/__init__.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'locative' -DEPENDENCIES = ['webhook'] - TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN) diff --git a/homeassistant/components/locative/device_tracker.py b/homeassistant/components/locative/device_tracker.py index 51135f4e21a941..1e16bde58ad485 100644 --- a/homeassistant/components/locative/device_tracker.py +++ b/homeassistant/components/locative/device_tracker.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['locative'] - DATA_KEY = '{}.{}'.format(LOCATIVE_DOMAIN, DEVICE_TRACKER_DOMAIN) diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index fe5286ba813daf..598de7961a51f8 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -19,7 +19,6 @@ ATTR_CHANGED_BY = 'changed_by' DOMAIN = 'lock' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_ALL_LOCKS = group.ENTITY_ID_FORMAT.format('all_locks') diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 7a0fb5e2654b3a..70fe31e64d6808 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -31,8 +31,6 @@ CONF_ENTITIES = 'entities' CONTINUOUS_DOMAINS = ['proximity', 'sensor'] -DEPENDENCIES = ['recorder', 'frontend'] - DOMAIN = 'logbook' GROUP_BY_MINUTES = 15 diff --git a/homeassistant/components/logi_circle/__init__.py b/homeassistant/components/logi_circle/__init__.py index 433895293f441d..1b74a9df03b3b0 100644 --- a/homeassistant/components/logi_circle/__init__.py +++ b/homeassistant/components/logi_circle/__init__.py @@ -20,8 +20,6 @@ RECORDING_MODE_KEY, SIGNAL_LOGI_CIRCLE_RECONFIGURE, SIGNAL_LOGI_CIRCLE_RECORD, SIGNAL_LOGI_CIRCLE_SNAPSHOT) -REQUIREMENTS = ['logi_circle==0.2.2'] - NOTIFICATION_ID = 'logi_circle_notification' NOTIFICATION_TITLE = 'Logi Circle Setup' diff --git a/homeassistant/components/logi_circle/camera.py b/homeassistant/components/logi_circle/camera.py index b69f23ac19dc7e..09baaa5ba0b3e8 100644 --- a/homeassistant/components/logi_circle/camera.py +++ b/homeassistant/components/logi_circle/camera.py @@ -15,8 +15,6 @@ RECORDING_MODE_KEY, SIGNAL_LOGI_CIRCLE_RECONFIGURE, SIGNAL_LOGI_CIRCLE_RECORD, SIGNAL_LOGI_CIRCLE_SNAPSHOT) -DEPENDENCIES = ['logi_circle', 'ffmpeg'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=60) diff --git a/homeassistant/components/logi_circle/sensor.py b/homeassistant/components/logi_circle/sensor.py index 01d5492eea7ce7..6efd5065ba6baa 100644 --- a/homeassistant/components/logi_circle/sensor.py +++ b/homeassistant/components/logi_circle/sensor.py @@ -11,8 +11,6 @@ from .const import ( ATTRIBUTION, DOMAIN as LOGI_CIRCLE_DOMAIN, LOGI_SENSORS as SENSOR_TYPES) -DEPENDENCIES = ['logi_circle'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/london_underground/sensor.py b/homeassistant/components/london_underground/sensor.py index c2502e2ab2be60..9bee85697928db 100644 --- a/homeassistant/components/london_underground/sensor.py +++ b/homeassistant/components/london_underground/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['london-tube-status==0.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Powered by TfL Open Data" diff --git a/homeassistant/components/loopenergy/sensor.py b/homeassistant/components/loopenergy/sensor.py index 23bdf48f64506d..b2afc36b8f5f3b 100644 --- a/homeassistant/components/loopenergy/sensor.py +++ b/homeassistant/components/loopenergy/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyloopenergy==0.1.2'] - CONF_ELEC = 'electricity' CONF_GAS = 'gas' diff --git a/homeassistant/components/luci/device_tracker.py b/homeassistant/components/luci/device_tracker.py index 77273d89d42033..4068be840c895a 100644 --- a/homeassistant/components/luci/device_tracker.py +++ b/homeassistant/components/luci/device_tracker.py @@ -9,8 +9,6 @@ CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['openwrt-luci-rpc==1.0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = False diff --git a/homeassistant/components/luftdaten/__init__.py b/homeassistant/components/luftdaten/__init__.py index 125cefb90265d6..81b177f734ae34 100644 --- a/homeassistant/components/luftdaten/__init__.py +++ b/homeassistant/components/luftdaten/__init__.py @@ -17,8 +17,6 @@ from .config_flow import configured_sensors, duplicate_stations from .const import CONF_SENSOR_ID, DEFAULT_SCAN_INTERVAL, DOMAIN -REQUIREMENTS = ['luftdaten==0.3.4'] - _LOGGER = logging.getLogger(__name__) DATA_LUFTDATEN = 'luftdaten' diff --git a/homeassistant/components/luftdaten/sensor.py b/homeassistant/components/luftdaten/sensor.py index 107673bac45214..ca68075df5d69c 100644 --- a/homeassistant/components/luftdaten/sensor.py +++ b/homeassistant/components/luftdaten/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['luftdaten'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lupusec/__init__.py b/homeassistant/components/lupusec/__init__.py index 8a5f098f741929..e97344f3082d11 100644 --- a/homeassistant/components/lupusec/__init__.py +++ b/homeassistant/components/lupusec/__init__.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['lupupy==0.0.17'] - DOMAIN = 'lupusec' NOTIFICATION_ID = 'lupusec_notification' diff --git a/homeassistant/components/lupusec/alarm_control_panel.py b/homeassistant/components/lupusec/alarm_control_panel.py index 0a88f3bd552fc4..9f3e7263396503 100644 --- a/homeassistant/components/lupusec/alarm_control_panel.py +++ b/homeassistant/components/lupusec/alarm_control_panel.py @@ -8,8 +8,6 @@ from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice -DEPENDENCIES = ['lupusec'] - ICON = 'mdi:security' SCAN_INTERVAL = timedelta(seconds=2) diff --git a/homeassistant/components/lupusec/binary_sensor.py b/homeassistant/components/lupusec/binary_sensor.py index 2c3f5e0e0b86e0..28833b3d246e2f 100644 --- a/homeassistant/components/lupusec/binary_sensor.py +++ b/homeassistant/components/lupusec/binary_sensor.py @@ -7,8 +7,6 @@ from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice -DEPENDENCIES = ['lupusec'] - SCAN_INTERVAL = timedelta(seconds=2) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lupusec/switch.py b/homeassistant/components/lupusec/switch.py index 0d86ea0a3650bf..b6391959397a07 100644 --- a/homeassistant/components/lupusec/switch.py +++ b/homeassistant/components/lupusec/switch.py @@ -6,8 +6,6 @@ from . import DOMAIN as LUPUSEC_DOMAIN, LupusecDevice -DEPENDENCIES = ['lupusec'] - SCAN_INTERVAL = timedelta(seconds=2) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron/__init__.py b/homeassistant/components/lutron/__init__.py index f642e96d8f6433..c91103f22446df 100644 --- a/homeassistant/components/lutron/__init__.py +++ b/homeassistant/components/lutron/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import slugify -REQUIREMENTS = ['pylutron==0.2.0'] - DOMAIN = 'lutron' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lutron/cover.py b/homeassistant/components/lutron/cover.py index da7f69095fc436..4a2d72d31160bc 100644 --- a/homeassistant/components/lutron/cover.py +++ b/homeassistant/components/lutron/cover.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron shades.""" diff --git a/homeassistant/components/lutron/light.py b/homeassistant/components/lutron/light.py index 5f3fd4787fd3ef..6ddf54e1fc1e50 100644 --- a/homeassistant/components/lutron/light.py +++ b/homeassistant/components/lutron/light.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron lights.""" diff --git a/homeassistant/components/lutron/scene.py b/homeassistant/components/lutron/scene.py index a2d18c6d242407..05deeef260d52b 100644 --- a/homeassistant/components/lutron/scene.py +++ b/homeassistant/components/lutron/scene.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron scenes.""" diff --git a/homeassistant/components/lutron/switch.py b/homeassistant/components/lutron/switch.py index b42c0d930bc834..0b1705fb235228 100644 --- a/homeassistant/components/lutron/switch.py +++ b/homeassistant/components/lutron/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Lutron switches.""" diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 61c005f60b2d22..516b5ccd7c864f 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -8,8 +8,6 @@ from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pylutron-caseta==0.5.0'] - _LOGGER = logging.getLogger(__name__) LUTRON_CASETA_SMARTBRIDGE = 'lutron_smartbridge' diff --git a/homeassistant/components/lutron_caseta/cover.py b/homeassistant/components/lutron_caseta/cover.py index d970f5282ff003..8793fc0236e2d1 100644 --- a/homeassistant/components/lutron_caseta/cover.py +++ b/homeassistant/components/lutron_caseta/cover.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron_caseta'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lutron_caseta/light.py b/homeassistant/components/lutron_caseta/light.py index d883da73c9161a..af93a459031e9b 100644 --- a/homeassistant/components/lutron_caseta/light.py +++ b/homeassistant/components/lutron_caseta/light.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron_caseta'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lutron_caseta/scene.py b/homeassistant/components/lutron_caseta/scene.py index 2e7059a56fc0a2..df0bb6a7a5a949 100644 --- a/homeassistant/components/lutron_caseta/scene.py +++ b/homeassistant/components/lutron_caseta/scene.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron_caseta'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lutron_caseta/switch.py b/homeassistant/components/lutron_caseta/switch.py index 54c670913576ce..0ccf625f765a56 100644 --- a/homeassistant/components/lutron_caseta/switch.py +++ b/homeassistant/components/lutron_caseta/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['lutron_caseta'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/lw12wifi/light.py b/homeassistant/components/lw12wifi/light.py index 5d9b7635ad2dd1..a2ff77dc2d0fda 100644 --- a/homeassistant/components/lw12wifi/light.py +++ b/homeassistant/components/lw12wifi/light.py @@ -16,8 +16,6 @@ import homeassistant.util.color as color_util -REQUIREMENTS = ['lw12==0.9.2'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/lyft/sensor.py b/homeassistant/components/lyft/sensor.py index 98d79cd970b965..b5788e50b33835 100644 --- a/homeassistant/components/lyft/sensor.py +++ b/homeassistant/components/lyft/sensor.py @@ -9,8 +9,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['lyft_rides==0.2'] - _LOGGER = logging.getLogger(__name__) CONF_CLIENT_ID = 'client_id' diff --git a/homeassistant/components/magicseaweed/sensor.py b/homeassistant/components/magicseaweed/sensor.py index 4c09d1e09e04f0..772cfb073c971a 100644 --- a/homeassistant/components/magicseaweed/sensor.py +++ b/homeassistant/components/magicseaweed/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['magicseaweed==1.0.3'] - _LOGGER = logging.getLogger(__name__) CONF_HOURS = 'hours' diff --git a/homeassistant/components/mailbox/__init__.py b/homeassistant/components/mailbox/__init__.py index 1907a1e9e978a7..8f8511464641ce 100644 --- a/homeassistant/components/mailbox/__init__.py +++ b/homeassistant/components/mailbox/__init__.py @@ -18,7 +18,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] DOMAIN = 'mailbox' EVENT = 'mailbox_updated' diff --git a/homeassistant/components/mailgun/__init__.py b/homeassistant/components/mailgun/__init__.py index 2a941d8bf505b8..2f89904f12b6be 100644 --- a/homeassistant/components/mailgun/__init__.py +++ b/homeassistant/components/mailgun/__init__.py @@ -15,7 +15,6 @@ CONF_SANDBOX = 'sandbox' DEFAULT_SANDBOX = False -DEPENDENCIES = ['webhook'] DOMAIN = 'mailgun' MESSAGE_RECEIVED = '{}_message_received'.format(DOMAIN) diff --git a/homeassistant/components/mailgun/notify.py b/homeassistant/components/mailgun/notify.py index b9f5bf0b10000f..4709f87b70c86c 100644 --- a/homeassistant/components/mailgun/notify.py +++ b/homeassistant/components/mailgun/notify.py @@ -11,12 +11,8 @@ from . import CONF_SANDBOX, DOMAIN as MAILGUN_DOMAIN -REQUIREMENTS = ['pymailgunner==1.4'] - _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mailgun'] - # Images to attach to notification ATTR_IMAGES = 'images' diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index 8057a8993473f1..d952dd68ebb2f6 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -83,8 +83,6 @@ def _state_schema(state): return vol.Schema(schema) -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ vol.Required(CONF_PLATFORM): 'manual_mqtt', vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string, diff --git a/homeassistant/components/mastodon/notify.py b/homeassistant/components/mastodon/notify.py index c1a91b8312ea1f..d4b78cc4e9fad7 100644 --- a/homeassistant/components/mastodon/notify.py +++ b/homeassistant/components/mastodon/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import (PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['Mastodon.py==1.3.1'] - _LOGGER = logging.getLogger(__name__) CONF_BASE_URL = 'base_url' diff --git a/homeassistant/components/matrix/__init__.py b/homeassistant/components/matrix/__init__.py index 4b3c1bf4d76965..0090d6eb62fa5e 100644 --- a/homeassistant/components/matrix/__init__.py +++ b/homeassistant/components/matrix/__init__.py @@ -14,8 +14,6 @@ from homeassistant.util.json import load_json, save_json from homeassistant.exceptions import HomeAssistantError -REQUIREMENTS = ['matrix-client==0.2.0'] - _LOGGER = logging.getLogger(__name__) SESSION_FILE = '.matrix.conf' diff --git a/homeassistant/components/matrix/notify.py b/homeassistant/components/matrix/notify.py index f1f53268c2ba85..de2ac3bda2a0b0 100644 --- a/homeassistant/components/matrix/notify.py +++ b/homeassistant/components/matrix/notify.py @@ -13,8 +13,6 @@ CONF_DEFAULT_ROOM = 'default_room' DOMAIN = 'matrix' -DEPENDENCIES = [DOMAIN] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_DEFAULT_ROOM): cv.string, }) diff --git a/homeassistant/components/maxcube/__init__.py b/homeassistant/components/maxcube/__init__.py index c398ccbde4f41e..12a6fda2cc3b2f 100644 --- a/homeassistant/components/maxcube/__init__.py +++ b/homeassistant/components/maxcube/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL -REQUIREMENTS = ['maxcube-api==0.1.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_PORT = 62910 diff --git a/homeassistant/components/media_extractor/__init__.py b/homeassistant/components/media_extractor/__init__.py index f44075816affd2..98f03cd8fd099c 100644 --- a/homeassistant/components/media_extractor/__init__.py +++ b/homeassistant/components/media_extractor/__init__.py @@ -12,15 +12,12 @@ ATTR_ENTITY_ID) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2019.04.07'] - _LOGGER = logging.getLogger(__name__) CONF_CUSTOMIZE_ENTITIES = 'customize' CONF_DEFAULT_STREAM_QUERY = 'default_query' DEFAULT_STREAM_QUERY = 'best' -DEPENDENCIES = ['media_player'] DOMAIN = 'media_extractor' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 5bc2d640e2bd56..7dcfdac52179f8 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -51,8 +51,6 @@ _LOGGER = logging.getLogger(__name__) _RND = SystemRandom() -DEPENDENCIES = ['http'] - ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_IMAGE_URL = '/api/media_player_proxy/{0}?token={1}&cache={2}' diff --git a/homeassistant/components/mediaroom/media_player.py b/homeassistant/components/mediaroom/media_player.py index acbc0462722a59..75aa20daf825b9 100644 --- a/homeassistant/components/mediaroom/media_player.py +++ b/homeassistant/components/mediaroom/media_player.py @@ -19,8 +19,6 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, dispatcher_send) -REQUIREMENTS = ['pymediaroom==0.6.4'] - _LOGGER = logging.getLogger(__name__) DATA_MEDIAROOM = 'mediaroom_known_stb' diff --git a/homeassistant/components/melissa/__init__.py b/homeassistant/components/melissa/__init__.py index 2037caa11c334f..14ecfadb5bf134 100644 --- a/homeassistant/components/melissa/__init__.py +++ b/homeassistant/components/melissa/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ["py-melissa-climate==2.0.0"] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'melissa' diff --git a/homeassistant/components/melissa/climate.py b/homeassistant/components/melissa/climate.py index 79d94a419912a5..8d834691b12932 100644 --- a/homeassistant/components/melissa/climate.py +++ b/homeassistant/components/melissa/climate.py @@ -13,8 +13,6 @@ from . import DATA_MELISSA -DEPENDENCIES = ['melissa'] - _LOGGER = logging.getLogger(__name__) SUPPORT_FLAGS = (SUPPORT_FAN_MODE | SUPPORT_OPERATION_MODE | diff --git a/homeassistant/components/meraki/device_tracker.py b/homeassistant/components/meraki/device_tracker.py index d12aff1127a0d9..edca1fbd494ca7 100644 --- a/homeassistant/components/meraki/device_tracker.py +++ b/homeassistant/components/meraki/device_tracker.py @@ -18,7 +18,6 @@ CONF_VALIDATOR = 'validator' CONF_SECRET = 'secret' -DEPENDENCIES = ['http'] URL = '/api/meraki' VERSION = '2.0' diff --git a/homeassistant/components/message_bird/notify.py b/homeassistant/components/message_bird/notify.py index c801de34a9a6ae..eecd563dc53f53 100644 --- a/homeassistant/components/message_bird/notify.py +++ b/homeassistant/components/message_bird/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import (ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['messagebird==1.2.0'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/met/weather.py b/homeassistant/components/met/weather.py index 6c9613ac5d2838..d9824e203c5462 100644 --- a/homeassistant/components/met/weather.py +++ b/homeassistant/components/met/weather.py @@ -13,8 +13,6 @@ async_call_later, async_track_utc_time_change) import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyMetno==0.4.6'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Weather forecast from met.no, delivered by the Norwegian " \ diff --git a/homeassistant/components/meteo_france/__init__.py b/homeassistant/components/meteo_france/__init__.py index e084cff3c79c73..df0292ec407db5 100644 --- a/homeassistant/components/meteo_france/__init__.py +++ b/homeassistant/components/meteo_france/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.util import Throttle -REQUIREMENTS = ['meteofrance==0.3.4'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Météo-France" diff --git a/homeassistant/components/metoffice/sensor.py b/homeassistant/components/metoffice/sensor.py index 6c4e91517dac6f..ff334823ec6f84 100644 --- a/homeassistant/components/metoffice/sensor.py +++ b/homeassistant/components/metoffice/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['datapoint==0.4.3'] - ATTR_LAST_UPDATE = 'last_update' ATTR_SENSOR_ID = 'sensor_id' ATTR_SITE_ID = 'site_id' diff --git a/homeassistant/components/metoffice/weather.py b/homeassistant/components/metoffice/weather.py index a67dcdcdbd67c7..409fc0991226a2 100644 --- a/homeassistant/components/metoffice/weather.py +++ b/homeassistant/components/metoffice/weather.py @@ -10,8 +10,6 @@ from .sensor import ATTRIBUTION, CONDITION_CLASSES, MetOfficeCurrentData -REQUIREMENTS = ['datapoint==0.4.3'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "Met Office" diff --git a/homeassistant/components/mfi/sensor.py b/homeassistant/components/mfi/sensor.py index 36f9d1a829c25c..49ec86c93cd693 100644 --- a/homeassistant/components/mfi/sensor.py +++ b/homeassistant/components/mfi/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['mficlient==0.3.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = True diff --git a/homeassistant/components/mfi/switch.py b/homeassistant/components/mfi/switch.py index 818081f7a2ed69..7b51813589dbbf 100644 --- a/homeassistant/components/mfi/switch.py +++ b/homeassistant/components/mfi/switch.py @@ -10,8 +10,6 @@ CONF_VERIFY_SSL) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['mficlient==0.3.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = True diff --git a/homeassistant/components/mhz19/sensor.py b/homeassistant/components/mhz19/sensor.py index 3aa82950fa7b8a..16e9da304a758c 100644 --- a/homeassistant/components/mhz19/sensor.py +++ b/homeassistant/components/mhz19/sensor.py @@ -12,8 +12,6 @@ from homeassistant.util.temperature import celsius_to_fahrenheit from homeassistant.util import Throttle -REQUIREMENTS = ['pmsensor==0.4'] - _LOGGER = logging.getLogger(__name__) CONF_SERIAL_DEVICE = 'serial_device' diff --git a/homeassistant/components/microsoft/tts.py b/homeassistant/components/microsoft/tts.py index 9fe31ef495eced..39bd1186b76e25 100644 --- a/homeassistant/components/microsoft/tts.py +++ b/homeassistant/components/microsoft/tts.py @@ -15,8 +15,6 @@ CONF_PITCH = 'pitch' CONF_CONTOUR = 'contour' -REQUIREMENTS = ["pycsspeechtts==1.0.2"] - _LOGGER = logging.getLogger(__name__) SUPPORTED_LANGUAGES = [ diff --git a/homeassistant/components/microsoft_face/__init__.py b/homeassistant/components/microsoft_face/__init__.py index 9b3ee960fb23c8..25b74698da6c81 100644 --- a/homeassistant/components/microsoft_face/__init__.py +++ b/homeassistant/components/microsoft_face/__init__.py @@ -25,7 +25,6 @@ DATA_MICROSOFT_FACE = 'microsoft_face' DEFAULT_TIMEOUT = 10 -DEPENDENCIES = ['camera'] DOMAIN = 'microsoft_face' FACE_API_URL = "api.cognitive.microsoft.com/face/v1.0/{0}" diff --git a/homeassistant/components/microsoft_face_detect/image_processing.py b/homeassistant/components/microsoft_face_detect/image_processing.py index 91eae07e9928bf..addcea21c86c5e 100644 --- a/homeassistant/components/microsoft_face_detect/image_processing.py +++ b/homeassistant/components/microsoft_face_detect/image_processing.py @@ -11,8 +11,6 @@ from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['microsoft_face'] - _LOGGER = logging.getLogger(__name__) SUPPORTED_ATTRIBUTES = [ diff --git a/homeassistant/components/microsoft_face_identify/image_processing.py b/homeassistant/components/microsoft_face_identify/image_processing.py index 52baa3617e82a4..055778be311a44 100644 --- a/homeassistant/components/microsoft_face_identify/image_processing.py +++ b/homeassistant/components/microsoft_face_identify/image_processing.py @@ -12,8 +12,6 @@ from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['microsoft_face'] - _LOGGER = logging.getLogger(__name__) CONF_GROUP = 'group' diff --git a/homeassistant/components/miflora/sensor.py b/homeassistant/components/miflora/sensor.py index 04595b0daeb148..0a8a51e0e80a90 100644 --- a/homeassistant/components/miflora/sensor.py +++ b/homeassistant/components/miflora/sensor.py @@ -11,8 +11,6 @@ CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START) from homeassistant.core import callback -REQUIREMENTS = ['miflora==0.4.0'] - _LOGGER = logging.getLogger(__name__) CONF_ADAPTER = 'adapter' diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index 7d376b431bbd82..0c3b6b313f1f0f 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -11,8 +11,6 @@ from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_SSL, CONF_METHOD) -REQUIREMENTS = ['librouteros==2.2.0'] - _LOGGER = logging.getLogger(__name__) MTK_DEFAULT_API_PORT = '8728' diff --git a/homeassistant/components/mill/climate.py b/homeassistant/components/mill/climate.py index cb6d47a52b0826..43877a1f818099 100644 --- a/homeassistant/components/mill/climate.py +++ b/homeassistant/components/mill/climate.py @@ -15,8 +15,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['millheater==0.3.4'] - _LOGGER = logging.getLogger(__name__) ATTR_AWAY_TEMP = 'away_temp' diff --git a/homeassistant/components/mitemp_bt/sensor.py b/homeassistant/components/mitemp_bt/sensor.py index cea2c6a55dbd6f..c2afaecf789ab2 100644 --- a/homeassistant/components/mitemp_bt/sensor.py +++ b/homeassistant/components/mitemp_bt/sensor.py @@ -12,8 +12,6 @@ ) -REQUIREMENTS = ['mitemp_bt==0.0.1'] - _LOGGER = logging.getLogger(__name__) CONF_ADAPTER = 'adapter' diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index a4ae78959cf367..711963a0b24bf3 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -15,10 +15,6 @@ from .webhook import handle_webhook from .websocket_api import register_websocket_handlers -DEPENDENCIES = ['device_tracker', 'http', 'webhook'] - -REQUIREMENTS = ['PyNaCl==1.3.0'] - async def async_setup(hass: HomeAssistantType, config: ConfigType): """Set up the mobile app component.""" diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 50943bb6504d8b..71d9fd9d58ab85 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -13,8 +13,6 @@ from .entity import MobileAppEntity, sensor_id -DEPENDENCIES = ['mobile_app'] - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up mobile app binary sensor from a config entry.""" diff --git a/homeassistant/components/mobile_app/notify.py b/homeassistant/components/mobile_app/notify.py index 8d2ac1b97ecef4..a69c020cfc8c26 100644 --- a/homeassistant/components/mobile_app/notify.py +++ b/homeassistant/components/mobile_app/notify.py @@ -22,8 +22,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mobile_app'] - def push_registrations(hass): """Return a dictionary of push enabled registrations.""" diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index 64ad69c5758ecc..2e54c2f4f6c1d2 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -12,8 +12,6 @@ from .entity import MobileAppEntity, sensor_id -DEPENDENCIES = ['mobile_app'] - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up mobile app sensor from a config entry.""" diff --git a/homeassistant/components/mochad/__init__.py b/homeassistant/components/mochad/__init__.py index e10adf693fe70d..78d137c95ead9f 100644 --- a/homeassistant/components/mochad/__init__.py +++ b/homeassistant/components/mochad/__init__.py @@ -9,8 +9,6 @@ EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) from homeassistant.const import (CONF_HOST, CONF_PORT) -REQUIREMENTS = ['pymochad==0.2.0'] - _LOGGER = logging.getLogger(__name__) CONTROLLER = None diff --git a/homeassistant/components/mochad/light.py b/homeassistant/components/mochad/light.py index d2e1a567d27501..4a734be4ebd928 100644 --- a/homeassistant/components/mochad/light.py +++ b/homeassistant/components/mochad/light.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mochad'] - CONF_BRIGHTNESS_LEVELS = 'brightness_levels' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/mochad/switch.py b/homeassistant/components/mochad/switch.py index 03fd2db07bf2fe..a4fb46130f3be0 100644 --- a/homeassistant/components/mochad/switch.py +++ b/homeassistant/components/mochad/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mochad'] - PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): mochad.DOMAIN, diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index 0500a904cb9c86..7d882066260857 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -9,8 +9,6 @@ CONF_TYPE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pymodbus==1.5.2'] - _LOGGER = logging.getLogger(__name__) ATTR_ADDRESS = 'address' diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index 0c10548452a01d..3a17f3c198d371 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -15,8 +15,6 @@ CONF_COIL = 'coil' CONF_COILS = 'coils' -DEPENDENCIES = ['modbus'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_COILS): [{ vol.Required(CONF_COIL): cv.positive_int, diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index 4d2b86903e770d..cf7e295092308b 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -22,8 +22,6 @@ DATA_TYPE_INT = 'int' DATA_TYPE_UINT = 'uint' DATA_TYPE_FLOAT = 'float' -DEPENDENCIES = ['modbus'] - SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index 10e11a9a656263..bca5ef9d34d168 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -29,8 +29,6 @@ DATA_TYPE_INT = 'int' DATA_TYPE_UINT = 'uint' -DEPENDENCIES = ['modbus'] - REGISTER_TYPE_HOLDING = 'holding' REGISTER_TYPE_INPUT = 'input' diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index 69c5e3e483888a..d74145ebad46ef 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -24,8 +24,6 @@ CONF_VERIFY_REGISTER = 'verify_register' CONF_VERIFY_STATE = 'verify_state' -DEPENDENCIES = ['modbus'] - REGISTER_TYPE_HOLDING = 'holding' REGISTER_TYPE_INPUT = 'input' diff --git a/homeassistant/components/modem_callerid/sensor.py b/homeassistant/components/modem_callerid/sensor.py index b87f4840334d4f..0e1f02efecfeeb 100644 --- a/homeassistant/components/modem_callerid/sensor.py +++ b/homeassistant/components/modem_callerid/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['basicmodem==0.7'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Modem CallerID' ICON = 'mdi:phone-classic' diff --git a/homeassistant/components/monoprice/media_player.py b/homeassistant/components/monoprice/media_player.py index edffd6ac7ce825..d8f22a5d00bac6 100644 --- a/homeassistant/components/monoprice/media_player.py +++ b/homeassistant/components/monoprice/media_player.py @@ -13,8 +13,6 @@ ATTR_ENTITY_ID, CONF_NAME, CONF_PORT, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pymonoprice==0.3'] - _LOGGER = logging.getLogger(__name__) SUPPORT_MONOPRICE = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ diff --git a/homeassistant/components/mopar/__init__.py b/homeassistant/components/mopar/__init__.py index 4ee9f3219b483a..ec723b94fcc76b 100644 --- a/homeassistant/components/mopar/__init__.py +++ b/homeassistant/components/mopar/__init__.py @@ -18,8 +18,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['motorparts==1.1.0'] - DOMAIN = 'mopar' DATA_UPDATED = '{}_data_updated'.format(DOMAIN) diff --git a/homeassistant/components/mopar/lock.py b/homeassistant/components/mopar/lock.py index aa2e0161813188..5a41058bb53c2b 100644 --- a/homeassistant/components/mopar/lock.py +++ b/homeassistant/components/mopar/lock.py @@ -7,8 +7,6 @@ ) from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED -DEPENDENCIES = ['mopar'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mopar/sensor.py b/homeassistant/components/mopar/sensor.py index 0d6e5765fda9aa..f09c0bdbea9f30 100644 --- a/homeassistant/components/mopar/sensor.py +++ b/homeassistant/components/mopar/sensor.py @@ -10,7 +10,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -DEPENDENCIES = ['mopar'] ICON = 'mdi:car' diff --git a/homeassistant/components/mopar/switch.py b/homeassistant/components/mopar/switch.py index 352cdafbd417ab..4e1ff606100aff 100644 --- a/homeassistant/components/mopar/switch.py +++ b/homeassistant/components/mopar/switch.py @@ -5,8 +5,6 @@ from homeassistant.components.switch import SwitchDevice from homeassistant.const import STATE_ON, STATE_OFF -DEPENDENCIES = ['mopar'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index 8cbc1406e0bc4a..5340bc46b1262b 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -20,8 +20,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['python-mpd2==1.0.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'MPD' diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 4f9ad990105853..e226e966b09667 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -40,8 +40,6 @@ CONF_BROKER, CONF_DISCOVERY, DEFAULT_DISCOVERY, CONF_STATE_TOPIC, ATTR_DISCOVERY_HASH) -REQUIREMENTS = ['paho-mqtt==1.4.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'mqtt' diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 03a2ac8e3887fa..da3e2faf224229 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -37,8 +37,6 @@ DEFAULT_ARM_HOME = 'ARM_HOME' DEFAULT_DISARM = 'DISARM' DEFAULT_NAME = 'MQTT Alarm' -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_CODE): cv.string, vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 95daad9b262868..904a456fc466e7 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -29,8 +29,6 @@ DEFAULT_PAYLOAD_ON = 'ON' DEFAULT_FORCE_UPDATE = False -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index f651050b6c8596..0449bf79ca7691 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -23,8 +23,6 @@ CONF_TOPIC = 'topic' DEFAULT_NAME = 'MQTT Camera' -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic, diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 17d32984bb528d..6a8c4d83995af5 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -31,8 +31,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - DEFAULT_NAME = 'MQTT HVAC' CONF_POWER_COMMAND_TOPIC = 'power_command_topic' diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 5cb7300f0efcb4..e1ad21564b5bbd 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -25,8 +25,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - CONF_GET_POSITION_TOPIC = 'position_topic' CONF_SET_POSITION_TEMPLATE = 'set_position_template' CONF_SET_POSITION_TOPIC = 'set_position_topic' diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index 659c6315b2150a..25528471d64310 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -11,8 +11,6 @@ from . import CONF_QOS -DEPENDENCIES = ['mqtt'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend({ diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index d86390ee31de17..99aa68d19756aa 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -23,8 +23,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - CONF_STATE_VALUE_TEMPLATE = 'state_value_template' CONF_SPEED_STATE_TOPIC = 'speed_state_topic' CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic' diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 4ff6efb8643960..d115f07ce7ee4e 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - CONF_SCHEMA = 'schema' diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index d5aa4480139215..382effe837b50d 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -30,8 +30,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - CONF_BRIGHTNESS_COMMAND_TOPIC = 'brightness_command_topic' CONF_BRIGHTNESS_SCALE = 'brightness_scale' CONF_BRIGHTNESS_STATE_TOPIC = 'brightness_state_topic' diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index a52f3c58d0efc9..27c88edb15fd84 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -35,8 +35,6 @@ DOMAIN = 'mqtt_json' -DEPENDENCIES = ['mqtt'] - DEFAULT_BRIGHTNESS = False DEFAULT_COLOR_TEMP = False DEFAULT_EFFECT = False diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 49cba082401d16..ab9fb0e4454824 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -30,8 +30,6 @@ DOMAIN = 'mqtt_template' -DEPENDENCIES = ['mqtt'] - DEFAULT_NAME = 'MQTT Template Light' DEFAULT_OPTIMISTIC = False diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 235eacc94540cf..75db4c3742d18a 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -27,8 +27,6 @@ DEFAULT_OPTIMISTIC = False DEFAULT_PAYLOAD_LOCK = 'LOCK' DEFAULT_PAYLOAD_UNLOCK = 'UNLOCK' -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index b6419ea2c24b36..02dafdb57c1a01 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -32,8 +32,6 @@ DEFAULT_NAME = 'MQTT Sensor' DEFAULT_FORCE_UPDATE = False -DEPENDENCIES = ['mqtt'] - PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICE): mqtt.MQTT_ENTITY_DEVICE_INFO_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index d7d36add517f6e..8944aba2dae37a 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -8,12 +8,8 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['hbmqtt==0.9.4'] - _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] - # None allows custom config to be created through generate_config HBMQTT_CONFIG_SCHEMA = vol.Any(None, vol.Schema({ vol.Optional('auth'): vol.Schema({ diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index 20d28b6496ca8b..a9e3875aaea66b 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -22,8 +22,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - DEFAULT_NAME = 'MQTT Switch' DEFAULT_PAYLOAD_ON = 'ON' DEFAULT_PAYLOAD_OFF = 'OFF' diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index 23a5e34b3caff5..7d910f0ac89de8 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -22,8 +22,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - SERVICE_TO_STRING = { SUPPORT_TURN_ON: 'turn_on', SUPPORT_TURN_OFF: 'turn_off', diff --git a/homeassistant/components/mqtt_eventstream/__init__.py b/homeassistant/components/mqtt_eventstream/__init__.py index fb6a94f1870d73..0b54c8535a2073 100644 --- a/homeassistant/components/mqtt_eventstream/__init__.py +++ b/homeassistant/components/mqtt_eventstream/__init__.py @@ -15,8 +15,6 @@ from homeassistant.helpers.json import JSONEncoder DOMAIN = 'mqtt_eventstream' -DEPENDENCIES = ['mqtt'] - CONF_PUBLISH_TOPIC = 'publish_topic' CONF_SUBSCRIBE_TOPIC = 'subscribe_topic' CONF_PUBLISH_EVENTSTREAM_RECEIVED = 'publish_eventstream_received' diff --git a/homeassistant/components/mqtt_json/device_tracker.py b/homeassistant/components/mqtt_json/device_tracker.py index 6059b26bcbdc11..eed6f03615e7d5 100644 --- a/homeassistant/components/mqtt_json/device_tracker.py +++ b/homeassistant/components/mqtt_json/device_tracker.py @@ -13,8 +13,6 @@ CONF_DEVICES, ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_BATTERY_LEVEL) -DEPENDENCIES = ['mqtt'] - _LOGGER = logging.getLogger(__name__) GPS_JSON_PAYLOAD_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/mqtt_room/sensor.py b/homeassistant/components/mqtt_room/sensor.py index 961769711a4d6c..37ea2697da15be 100644 --- a/homeassistant/components/mqtt_room/sensor.py +++ b/homeassistant/components/mqtt_room/sensor.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['mqtt'] - ATTR_DEVICE_ID = 'device_id' ATTR_DISTANCE = 'distance' ATTR_ROOM = 'room' diff --git a/homeassistant/components/mqtt_statestream/__init__.py b/homeassistant/components/mqtt_statestream/__init__.py index 18a70bf75bb3a8..0d594822e05764 100644 --- a/homeassistant/components/mqtt_statestream/__init__.py +++ b/homeassistant/components/mqtt_statestream/__init__.py @@ -16,7 +16,6 @@ CONF_PUBLISH_ATTRIBUTES = 'publish_attributes' CONF_PUBLISH_TIMESTAMPS = 'publish_timestamps' -DEPENDENCIES = ['mqtt'] DOMAIN = 'mqtt_statestream' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/mvglive/sensor.py b/homeassistant/components/mvglive/sensor.py index 978c9ad34eb436..8c887031aa9eef 100644 --- a/homeassistant/components/mvglive/sensor.py +++ b/homeassistant/components/mvglive/sensor.py @@ -11,8 +11,6 @@ from homeassistant.const import ( CONF_NAME, ATTR_ATTRIBUTION) -REQUIREMENTS = ['PyMVGLive==1.1.4'] - _LOGGER = logging.getLogger(__name__) CONF_NEXT_DEPARTURE = 'nextdeparture' diff --git a/homeassistant/components/mychevy/__init__.py b/homeassistant/components/mychevy/__init__.py index e6fd7f19c2a3ab..b4235362ff26ab 100644 --- a/homeassistant/components/mychevy/__init__.py +++ b/homeassistant/components/mychevy/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers import discovery from homeassistant.util import Throttle -REQUIREMENTS = ['mychevy==1.2.0'] - DOMAIN = 'mychevy' UPDATE_TOPIC = DOMAIN ERROR_TOPIC = DOMAIN + "_error" diff --git a/homeassistant/components/mycroft/__init__.py b/homeassistant/components/mycroft/__init__.py index 29f6383f686b1e..fdcedfb7345195 100644 --- a/homeassistant/components/mycroft/__init__.py +++ b/homeassistant/components/mycroft/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['mycroftapi==2.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'mycroft' diff --git a/homeassistant/components/mycroft/notify.py b/homeassistant/components/mycroft/notify.py index d66be629f17b09..5918f16290ddc5 100644 --- a/homeassistant/components/mycroft/notify.py +++ b/homeassistant/components/mycroft/notify.py @@ -3,8 +3,6 @@ from homeassistant.components.notify import BaseNotificationService -DEPENDENCIES = ['mycroft'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/myq/cover.py b/homeassistant/components/myq/cover.py index 5b926a183f72c9..395e5d4e9596f0 100644 --- a/homeassistant/components/myq/cover.py +++ b/homeassistant/components/myq/cover.py @@ -11,7 +11,6 @@ ) from homeassistant.helpers import aiohttp_client, config_validation as cv -REQUIREMENTS = ['pymyq==1.2.0'] _LOGGER = logging.getLogger(__name__) MYQ_TO_HASS = { diff --git a/homeassistant/components/mysensors/__init__.py b/homeassistant/components/mysensors/__init__.py index 7ca21ac582a001..12d210b50a3f7b 100644 --- a/homeassistant/components/mysensors/__init__.py +++ b/homeassistant/components/mysensors/__init__.py @@ -17,8 +17,6 @@ from .device import get_mysensors_devices from .gateway import get_mysensors_gateway, setup_gateways, finish_setup -REQUIREMENTS = ['pymysensors==0.18.0'] - _LOGGER = logging.getLogger(__name__) CONF_DEBUG = 'debug' diff --git a/homeassistant/components/mystrom/binary_sensor.py b/homeassistant/components/mystrom/binary_sensor.py index 42245dc4df3e85..d3b4dd554a99f2 100644 --- a/homeassistant/components/mystrom/binary_sensor.py +++ b/homeassistant/components/mystrom/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/mystrom/light.py b/homeassistant/components/mystrom/light.py index f9b8dcd203b60c..149b83b2487005 100644 --- a/homeassistant/components/mystrom/light.py +++ b/homeassistant/components/mystrom/light.py @@ -10,8 +10,6 @@ ATTR_HS_COLOR) from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME -REQUIREMENTS = ['python-mystrom==0.5.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'myStrom bulb' diff --git a/homeassistant/components/mystrom/switch.py b/homeassistant/components/mystrom/switch.py index a25517eea911db..3fbd6957eb97c5 100644 --- a/homeassistant/components/mystrom/switch.py +++ b/homeassistant/components/mystrom/switch.py @@ -7,8 +7,6 @@ from homeassistant.const import (CONF_NAME, CONF_HOST) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-mystrom==0.5.0'] - DEFAULT_NAME = 'myStrom Switch' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/mythicbeastsdns/__init__.py b/homeassistant/components/mythicbeastsdns/__init__.py index 4db53bf0407cf4..02441d9c650a35 100644 --- a/homeassistant/components/mythicbeastsdns/__init__.py +++ b/homeassistant/components/mythicbeastsdns/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['mbddns==0.1.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'mythicbeastsdns' diff --git a/homeassistant/components/n26/__init__.py b/homeassistant/components/n26/__init__.py index 8f4ade9c87f6ea..fb7084bffe7e1d 100644 --- a/homeassistant/components/n26/__init__.py +++ b/homeassistant/components/n26/__init__.py @@ -12,8 +12,6 @@ from .const import DATA, DOMAIN -REQUIREMENTS = ['n26==0.2.7'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SCAN_INTERVAL = timedelta(minutes=30) diff --git a/homeassistant/components/n26/sensor.py b/homeassistant/components/n26/sensor.py index 682cd5dae68575..be5ad7a1b687f8 100644 --- a/homeassistant/components/n26/sensor.py +++ b/homeassistant/components/n26/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['n26'] - SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL ATTR_IBAN = "account" diff --git a/homeassistant/components/n26/switch.py b/homeassistant/components/n26/switch.py index 0e7455ea7030c8..152212550971f4 100644 --- a/homeassistant/components/n26/switch.py +++ b/homeassistant/components/n26/switch.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['n26'] - SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL diff --git a/homeassistant/components/nad/media_player.py b/homeassistant/components/nad/media_player.py index 8c5a14a35243c8..60747fa63986e7 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -11,8 +11,6 @@ SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP) from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON, CONF_HOST -REQUIREMENTS = ['nad_receiver==0.0.11'] - _LOGGER = logging.getLogger(__name__) DEFAULT_TYPE = 'RS232' diff --git a/homeassistant/components/namecheapdns/__init__.py b/homeassistant/components/namecheapdns/__init__.py index f86e7d18556786..d3c48d568bdb71 100644 --- a/homeassistant/components/namecheapdns/__init__.py +++ b/homeassistant/components/namecheapdns/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['defusedxml==0.5.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'namecheapdns' diff --git a/homeassistant/components/nanoleaf/light.py b/homeassistant/components/nanoleaf/light.py index 60457e21f9a67d..017bd0a256dc5c 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -15,8 +15,6 @@ color_temperature_mired_to_kelvin as mired_to_kelvin from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['pynanoleaf==0.0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Nanoleaf' diff --git a/homeassistant/components/neato/__init__.py b/homeassistant/components/neato/__init__.py index bb717b8d230b22..f179248b5632b4 100644 --- a/homeassistant/components/neato/__init__.py +++ b/homeassistant/components/neato/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers import discovery from homeassistant.util import Throttle -REQUIREMENTS = ['pybotvac==0.0.13'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'neato' diff --git a/homeassistant/components/neato/camera.py b/homeassistant/components/neato/camera.py index f8106c3e645ebc..5d38e7b78809b5 100644 --- a/homeassistant/components/neato/camera.py +++ b/homeassistant/components/neato/camera.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['neato'] - SCAN_INTERVAL = timedelta(minutes=10) diff --git a/homeassistant/components/neato/switch.py b/homeassistant/components/neato/switch.py index ea60f9492e22a7..0721381a563acb 100644 --- a/homeassistant/components/neato/switch.py +++ b/homeassistant/components/neato/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['neato'] - SCAN_INTERVAL = timedelta(minutes=10) SWITCH_TYPE_SCHEDULE = 'schedule' diff --git a/homeassistant/components/neato/vacuum.py b/homeassistant/components/neato/vacuum.py index 3575301ea97e6b..061d8fd04c8166 100644 --- a/homeassistant/components/neato/vacuum.py +++ b/homeassistant/components/neato/vacuum.py @@ -21,8 +21,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['neato'] - SCAN_INTERVAL = timedelta(minutes=5) SUPPORT_NEATO = SUPPORT_BATTERY | SUPPORT_PAUSE | SUPPORT_RETURN_HOME | \ diff --git a/homeassistant/components/nederlandse_spoorwegen/sensor.py b/homeassistant/components/nederlandse_spoorwegen/sensor.py index 224d16e4869b9a..7fc3e438f38a55 100644 --- a/homeassistant/components/nederlandse_spoorwegen/sensor.py +++ b/homeassistant/components/nederlandse_spoorwegen/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['nsapi==2.7.4'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by NS" diff --git a/homeassistant/components/nello/lock.py b/homeassistant/components/nello/lock.py index efb7719e2013fd..124fa6769ec854 100644 --- a/homeassistant/components/nello/lock.py +++ b/homeassistant/components/nello/lock.py @@ -8,8 +8,6 @@ from homeassistant.components.lock import (LockDevice, PLATFORM_SCHEMA) from homeassistant.const import (CONF_PASSWORD, CONF_USERNAME) -REQUIREMENTS = ['pynello==2.0.2'] - _LOGGER = logging.getLogger(__name__) ATTR_ADDRESS = 'address' diff --git a/homeassistant/components/ness_alarm/__init__.py b/homeassistant/components/ness_alarm/__init__.py index 97896f9aa3f9ec..8d9d081e6d8247 100644 --- a/homeassistant/components/ness_alarm/__init__.py +++ b/homeassistant/components/ness_alarm/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['nessclient==0.9.15'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'ness_alarm' diff --git a/homeassistant/components/ness_alarm/alarm_control_panel.py b/homeassistant/components/ness_alarm/alarm_control_panel.py index 618297ef9a5216..06a3f9f1e13470 100644 --- a/homeassistant/components/ness_alarm/alarm_control_panel.py +++ b/homeassistant/components/ness_alarm/alarm_control_panel.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['ness_alarm'] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/ness_alarm/binary_sensor.py b/homeassistant/components/ness_alarm/binary_sensor.py index 2bed9eb64042bf..6d9486577a72f4 100644 --- a/homeassistant/components/ness_alarm/binary_sensor.py +++ b/homeassistant/components/ness_alarm/binary_sensor.py @@ -9,7 +9,6 @@ CONF_ZONE_ID, CONF_ZONE_NAME, CONF_ZONE_TYPE, CONF_ZONES, SIGNAL_ZONE_CHANGED, ZoneChangedData) -DEPENDENCIES = ['ness_alarm'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 21aaa2109a10c4..cc726cdf1754c4 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -22,8 +22,6 @@ from .const import DOMAIN from . import local_auth -REQUIREMENTS = ['python-nest==4.1.0'] - _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/nest/binary_sensor.py b/homeassistant/components/nest/binary_sensor.py index aa56bfbf29d50c..1fc8aa8929f330 100644 --- a/homeassistant/components/nest/binary_sensor.py +++ b/homeassistant/components/nest/binary_sensor.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nest'] - BINARY_TYPES = {'online': 'connectivity'} CLIMATE_BINARY_TYPES = { diff --git a/homeassistant/components/nest/camera.py b/homeassistant/components/nest/camera.py index 8b450e02b46777..029de178f24cc4 100644 --- a/homeassistant/components/nest/camera.py +++ b/homeassistant/components/nest/camera.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nest'] - NEST_BRAND = 'Nest' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({}) diff --git a/homeassistant/components/nest/climate.py b/homeassistant/components/nest/climate.py index cd9a7cb71b634d..4707d8d0f8c30c 100644 --- a/homeassistant/components/nest/climate.py +++ b/homeassistant/components/nest/climate.py @@ -16,7 +16,6 @@ from . import DATA_NEST, DOMAIN as NEST_DOMAIN, SIGNAL_NEST_UPDATE -DEPENDENCIES = ['nest'] _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py index ecae83e303c2a9..2bfeea897849d9 100644 --- a/homeassistant/components/nest/sensor.py +++ b/homeassistant/components/nest/sensor.py @@ -8,8 +8,6 @@ from . import CONF_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice -DEPENDENCIES = ['nest'] - SENSOR_TYPES = ['humidity', 'operation_mode', 'hvac_state'] TEMP_SENSOR_TYPES = ['temperature', 'target'] diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index 2036e55b3a88ad..cf64363ba503f9 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -12,9 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['pyatmo==1.9'] -DEPENDENCIES = ['webhook'] - _LOGGER = logging.getLogger(__name__) DATA_PERSONS = 'netatmo_persons' diff --git a/homeassistant/components/netatmo/binary_sensor.py b/homeassistant/components/netatmo/binary_sensor.py index 7c2b1a73a4dcd8..f282faf82c87aa 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['netatmo'] - # These are the available sensors mapped to binary_sensor class WELCOME_SENSOR_TYPES = { "Someone known": "motion", diff --git a/homeassistant/components/netatmo/camera.py b/homeassistant/components/netatmo/camera.py index c8a540be6dd950..b74dce4b26209b 100644 --- a/homeassistant/components/netatmo/camera.py +++ b/homeassistant/components/netatmo/camera.py @@ -11,8 +11,6 @@ from . import CameraData, NETATMO_AUTH -DEPENDENCIES = ['netatmo'] - _LOGGER = logging.getLogger(__name__) CONF_HOME = 'home' diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index 5defbbf22e3e33..00c08c654ef0f3 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -16,8 +16,6 @@ from . import NETATMO_AUTH -DEPENDENCIES = ['netatmo'] - _LOGGER = logging.getLogger(__name__) CONF_HOMES = 'homes' diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 2ce4b6e6ce2a38..c9c1101c2a2b1e 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -19,8 +19,6 @@ CONF_MODULES = 'modules' CONF_STATION = 'station' -DEPENDENCIES = ['netatmo'] - # This is the NetAtmo data upload interval in seconds NETATMO_UPDATE_INTERVAL = 600 diff --git a/homeassistant/components/netatmo_public/sensor.py b/homeassistant/components/netatmo_public/sensor.py index 3480534436da6b..814675ca8b7ae1 100644 --- a/homeassistant/components/netatmo_public/sensor.py +++ b/homeassistant/components/netatmo_public/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['netatmo'] - CONF_AREAS = 'areas' CONF_LAT_NE = 'lat_ne' CONF_LON_NE = 'lon_ne' diff --git a/homeassistant/components/netdata/sensor.py b/homeassistant/components/netdata/sensor.py index 6d99722a4162d4..eb6d6088ea8840 100644 --- a/homeassistant/components/netdata/sensor.py +++ b/homeassistant/components/netdata/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['netdata==0.1.2'] - _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) diff --git a/homeassistant/components/netgear/device_tracker.py b/homeassistant/components/netgear/device_tracker.py index ce8c2d6066d3d4..36921601cc2722 100644 --- a/homeassistant/components/netgear/device_tracker.py +++ b/homeassistant/components/netgear/device_tracker.py @@ -10,8 +10,6 @@ CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_SSL, CONF_DEVICES, CONF_EXCLUDE) -REQUIREMENTS = ['pynetgear==0.5.2'] - _LOGGER = logging.getLogger(__name__) CONF_APS = 'accesspoints' diff --git a/homeassistant/components/netgear_lte/__init__.py b/homeassistant/components/netgear_lte/__init__.py index c0f248a3dd5205..5491fffe96989f 100644 --- a/homeassistant/components/netgear_lte/__init__.py +++ b/homeassistant/components/netgear_lte/__init__.py @@ -24,8 +24,6 @@ from . import sensor_types -REQUIREMENTS = ['eternalegypt==0.0.7'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/netgear_lte/binary_sensor.py b/homeassistant/components/netgear_lte/binary_sensor.py index a26c8538ea5be9..b13e1b0bbb4d53 100644 --- a/homeassistant/components/netgear_lte/binary_sensor.py +++ b/homeassistant/components/netgear_lte/binary_sensor.py @@ -7,8 +7,6 @@ from . import CONF_MONITORED_CONDITIONS, DATA_KEY, LTEEntity from .sensor_types import BINARY_SENSOR_CLASSES -DEPENDENCIES = ['netgear_lte'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/netgear_lte/notify.py b/homeassistant/components/netgear_lte/notify.py index fba1a335ace333..cb71a7945e332d 100644 --- a/homeassistant/components/netgear_lte/notify.py +++ b/homeassistant/components/netgear_lte/notify.py @@ -8,8 +8,6 @@ from . import CONF_RECIPIENT, DATA_KEY -DEPENDENCIES = ['netgear_lte'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index 238a5f9b72d3d6..edf55480a68304 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -8,8 +8,6 @@ from .sensor_types import ( SENSOR_SMS, SENSOR_SMS_TOTAL, SENSOR_USAGE, SENSOR_UNITS) -DEPENDENCIES = ['netgear_lte'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/netio/switch.py b/homeassistant/components/netio/switch.py index 27a7dfbd5e7de5..ddaa9ffe0ff80a 100644 --- a/homeassistant/components/netio/switch.py +++ b/homeassistant/components/netio/switch.py @@ -14,8 +14,6 @@ from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pynetio==0.1.9.1'] - _LOGGER = logging.getLogger(__name__) ATTR_START_DATE = 'start_date' @@ -25,7 +23,6 @@ DEFAULT_PORT = 1234 DEFAULT_USERNAME = 'admin' -DEPENDENCIES = ['http'] Device = namedtuple('device', ['netio', 'entities']) DEVICES = {} diff --git a/homeassistant/components/neurio_energy/sensor.py b/homeassistant/components/neurio_energy/sensor.py index 9e12465c69b078..5992ca70593e14 100644 --- a/homeassistant/components/neurio_energy/sensor.py +++ b/homeassistant/components/neurio_energy/sensor.py @@ -13,8 +13,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['neurio==0.3.1'] - _LOGGER = logging.getLogger(__name__) CONF_API_SECRET = 'api_secret' diff --git a/homeassistant/components/niko_home_control/light.py b/homeassistant/components/niko_home_control/light.py index 00e8dc838a6c0d..b7ba5d33eb8e00 100644 --- a/homeassistant/components/niko_home_control/light.py +++ b/homeassistant/components/niko_home_control/light.py @@ -9,8 +9,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['niko-home-control==0.1.8'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/nilu/air_quality.py b/homeassistant/components/nilu/air_quality.py index 979d5736d6a20d..cdc099765214c8 100644 --- a/homeassistant/components/nilu/air_quality.py +++ b/homeassistant/components/nilu/air_quality.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['niluclient==0.1.2'] - _LOGGER = logging.getLogger(__name__) ATTR_AREA = 'area' diff --git a/homeassistant/components/nissan_leaf/__init__.py b/homeassistant/components/nissan_leaf/__init__.py index cb101c0a5309c5..f9e7cd7f2d1879 100644 --- a/homeassistant/components/nissan_leaf/__init__.py +++ b/homeassistant/components/nissan_leaf/__init__.py @@ -16,8 +16,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -REQUIREMENTS = ['pycarwings2==2.8'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'nissan_leaf' diff --git a/homeassistant/components/nissan_leaf/binary_sensor.py b/homeassistant/components/nissan_leaf/binary_sensor.py index 5c71cf1fc513a9..5456fdc913a51b 100644 --- a/homeassistant/components/nissan_leaf/binary_sensor.py +++ b/homeassistant/components/nissan_leaf/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nissan_leaf'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up of a Nissan Leaf binary sensor.""" diff --git a/homeassistant/components/nissan_leaf/device_tracker.py b/homeassistant/components/nissan_leaf/device_tracker.py index 95f6fcdcaf16c0..0e2dca25ca6842 100644 --- a/homeassistant/components/nissan_leaf/device_tracker.py +++ b/homeassistant/components/nissan_leaf/device_tracker.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nissan_leaf'] - ICON_CAR = "mdi:car" diff --git a/homeassistant/components/nissan_leaf/sensor.py b/homeassistant/components/nissan_leaf/sensor.py index 682f482b4888f2..064a96a64a1a53 100644 --- a/homeassistant/components/nissan_leaf/sensor.py +++ b/homeassistant/components/nissan_leaf/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nissan_leaf'] - ICON_RANGE = 'mdi:speedometer' diff --git a/homeassistant/components/nissan_leaf/switch.py b/homeassistant/components/nissan_leaf/switch.py index e6d72103a6c6c4..27f81b69dd7309 100644 --- a/homeassistant/components/nissan_leaf/switch.py +++ b/homeassistant/components/nissan_leaf/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['nissan_leaf'] - def setup_platform(hass, config, add_devices, discovery_info=None): """Nissan Leaf switch platform setup.""" diff --git a/homeassistant/components/nmap_tracker/device_tracker.py b/homeassistant/components/nmap_tracker/device_tracker.py index e553d323b72306..3537f01b2b86d2 100644 --- a/homeassistant/components/nmap_tracker/device_tracker.py +++ b/homeassistant/components/nmap_tracker/device_tracker.py @@ -13,8 +13,6 @@ DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOSTS -REQUIREMENTS = ['python-nmap==0.6.1'] - _LOGGER = logging.getLogger(__name__) CONF_EXCLUDE = 'exclude' diff --git a/homeassistant/components/nmbs/sensor.py b/homeassistant/components/nmbs/sensor.py index 034c37530b355e..799225968e5493 100644 --- a/homeassistant/components/nmbs/sensor.py +++ b/homeassistant/components/nmbs/sensor.py @@ -23,8 +23,6 @@ CONF_STATION_LIVE = 'station_live' CONF_EXCLUDE_VIAS = 'exclude_vias' -REQUIREMENTS = ["pyrail==0.0.3"] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_STATION_FROM): cv.string, vol.Required(CONF_STATION_TO): cv.string, diff --git a/homeassistant/components/noaa_tides/sensor.py b/homeassistant/components/noaa_tides/sensor.py index 0c4bde94f5770f..0749f13031f20c 100644 --- a/homeassistant/components/noaa_tides/sensor.py +++ b/homeassistant/components/noaa_tides/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['py_noaa==0.3.0'] - _LOGGER = logging.getLogger(__name__) CONF_STATION_ID = 'station_id' diff --git a/homeassistant/components/norway_air/air_quality.py b/homeassistant/components/norway_air/air_quality.py index 06ed68801f89c7..f2d5d87be47cb0 100644 --- a/homeassistant/components/norway_air/air_quality.py +++ b/homeassistant/components/norway_air/air_quality.py @@ -12,8 +12,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyMetno==0.4.6'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Air quality from " \ diff --git a/homeassistant/components/nsw_fuel_station/sensor.py b/homeassistant/components/nsw_fuel_station/sensor.py index ce4337fc93ab6c..9bb24973f45541 100644 --- a/homeassistant/components/nsw_fuel_station/sensor.py +++ b/homeassistant/components/nsw_fuel_station/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['nsw-fuel-api-client==1.0.10'] - _LOGGER = logging.getLogger(__name__) ATTR_STATION_ID = 'station_id' diff --git a/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py b/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py index 38491feb32f406..7a6d681bfbb205 100644 --- a/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py +++ b/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py @@ -16,8 +16,6 @@ async_dispatcher_connect, dispatcher_send) from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['geojson_client==0.3'] - _LOGGER = logging.getLogger(__name__) ATTR_CATEGORY = 'category' diff --git a/homeassistant/components/nuheat/__init__.py b/homeassistant/components/nuheat/__init__.py index 4ea37339ef35f3..f8227391ffd834 100644 --- a/homeassistant/components/nuheat/__init__.py +++ b/homeassistant/components/nuheat/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery -REQUIREMENTS = ["nuheat==0.3.0"] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'nuheat' diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index 32adc1d216f3c5..6a391679b89826 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -15,8 +15,6 @@ from . import DOMAIN as NUHEAT_DOMAIN -DEPENDENCIES = ["nuheat"] - _LOGGER = logging.getLogger(__name__) ICON = "mdi:thermometer" diff --git a/homeassistant/components/nuimo_controller/__init__.py b/homeassistant/components/nuimo_controller/__init__.py index 70509469d2bcf9..ca1de204a395c2 100644 --- a/homeassistant/components/nuimo_controller/__init__.py +++ b/homeassistant/components/nuimo_controller/__init__.py @@ -8,10 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import (CONF_MAC, CONF_NAME, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = [ - '--only-binary=all ' # avoid compilation of gattlib - 'nuimo==0.1.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'nuimo_controller' diff --git a/homeassistant/components/nuki/lock.py b/homeassistant/components/nuki/lock.py index ef49d4b97dd955..0d0452378583b7 100644 --- a/homeassistant/components/nuki/lock.py +++ b/homeassistant/components/nuki/lock.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.service import extract_entity_ids -REQUIREMENTS = ['pynuki==1.3.2'] - _LOGGER = logging.getLogger(__name__) DEFAULT_PORT = 8080 diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 43ba06f70eb1b3..1a4ce779878119 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pynut2==2.1.2'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'NUT UPS' diff --git a/homeassistant/components/nx584/alarm_control_panel.py b/homeassistant/components/nx584/alarm_control_panel.py index c5e1fede6fd8fd..4c6c604c950b5f 100644 --- a/homeassistant/components/nx584/alarm_control_panel.py +++ b/homeassistant/components/nx584/alarm_control_panel.py @@ -11,8 +11,6 @@ STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pynx584==0.4'] - _LOGGER = logging.getLogger(__name__) DEFAULT_HOST = 'localhost' diff --git a/homeassistant/components/nx584/binary_sensor.py b/homeassistant/components/nx584/binary_sensor.py index 61f8fb801eac5b..2162d7420221e3 100644 --- a/homeassistant/components/nx584/binary_sensor.py +++ b/homeassistant/components/nx584/binary_sensor.py @@ -11,8 +11,6 @@ from homeassistant.const import (CONF_HOST, CONF_PORT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pynx584==0.4'] - _LOGGER = logging.getLogger(__name__) CONF_EXCLUDE_ZONES = 'exclude_zones' diff --git a/homeassistant/components/oasa_telematics/sensor.py b/homeassistant/components/oasa_telematics/sensor.py index 665f2f83f86af7..60c2f9a231b991 100644 --- a/homeassistant/components/oasa_telematics/sensor.py +++ b/homeassistant/components/oasa_telematics/sensor.py @@ -12,7 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import dt as dt_util -REQUIREMENTS = ['oasatelematics==0.3'] _LOGGER = logging.getLogger(__name__) ATTR_STOP_ID = 'stop_id' diff --git a/homeassistant/components/octoprint/binary_sensor.py b/homeassistant/components/octoprint/binary_sensor.py index be3381f3bc8a2e..d505c88071ee3d 100644 --- a/homeassistant/components/octoprint/binary_sensor.py +++ b/homeassistant/components/octoprint/binary_sensor.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['octoprint'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the available OctoPrint binary sensors.""" diff --git a/homeassistant/components/octoprint/sensor.py b/homeassistant/components/octoprint/sensor.py index f07d88d11da3ff..979f56290c16d6 100644 --- a/homeassistant/components/octoprint/sensor.py +++ b/homeassistant/components/octoprint/sensor.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['octoprint'] - NOTIFICATION_ID = 'octoprint_notification' NOTIFICATION_TITLE = 'OctoPrint sensor setup error' diff --git a/homeassistant/components/oem/climate.py b/homeassistant/components/oem/climate.py index f1e03396b05002..3ae9b4dad5c93e 100644 --- a/homeassistant/components/oem/climate.py +++ b/homeassistant/components/oem/climate.py @@ -21,8 +21,6 @@ CONF_PORT, TEMP_CELSIUS, CONF_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['oemthermostat==1.1'] - _LOGGER = logging.getLogger(__name__) CONF_AWAY_TEMP = 'away_temp' diff --git a/homeassistant/components/ohmconnect/sensor.py b/homeassistant/components/ohmconnect/sensor.py index 1d870e4d15a12c..87dca2aa853b09 100644 --- a/homeassistant/components/ohmconnect/sensor.py +++ b/homeassistant/components/ohmconnect/sensor.py @@ -11,8 +11,6 @@ from homeassistant.util import Throttle from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['defusedxml==0.5.0'] - _LOGGER = logging.getLogger(__name__) CONF_ID = 'id' diff --git a/homeassistant/components/onboarding/__init__.py b/homeassistant/components/onboarding/__init__.py index f8885962ee7174..29371369c70506 100644 --- a/homeassistant/components/onboarding/__init__.py +++ b/homeassistant/components/onboarding/__init__.py @@ -4,8 +4,6 @@ from .const import DOMAIN, STEP_USER, STEPS -DEPENDENCIES = ['auth', 'http'] - STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index 64b9684c58c4ec..0a8a459731e13f 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -16,8 +16,6 @@ CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, ATTR_ENTITY_ID) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['onkyo-eiscp==1.2.4'] - _LOGGER = logging.getLogger(__name__) CONF_SOURCES = 'sources' diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 90222b9cafc06b..6a773a854c9809 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -20,10 +20,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['onvif-py3==0.1.3', - 'suds-py3==1.3.3.0', - 'suds-passworddigest-homeassistant==0.1.2a0.dev0'] -DEPENDENCIES = ['ffmpeg'] DEFAULT_NAME = 'ONVIF Camera' DEFAULT_PORT = 5000 DEFAULT_USERNAME = 'admin' diff --git a/homeassistant/components/opencv/image_processing.py b/homeassistant/components/opencv/image_processing.py index 10173cdb725c8b..4a28a37b7056d4 100644 --- a/homeassistant/components/opencv/image_processing.py +++ b/homeassistant/components/opencv/image_processing.py @@ -11,8 +11,6 @@ from homeassistant.core import split_entity_id import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['numpy==1.16.2'] - _LOGGER = logging.getLogger(__name__) ATTR_MATCHES = 'matches' diff --git a/homeassistant/components/openevse/sensor.py b/homeassistant/components/openevse/sensor.py index e54b47236c5fe3..efc4f8a020049a 100644 --- a/homeassistant/components/openevse/sensor.py +++ b/homeassistant/components/openevse/sensor.py @@ -11,8 +11,6 @@ CONF_MONITORED_VARIABLES) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['openevsewifi==0.4'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index 03926bce8c5ce8..edb033b8f11c91 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -10,8 +10,6 @@ from homeassistant.const import ( STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) -REQUIREMENTS = ['openhomedevice==0.4.2'] - SUPPORT_OPENHOME = SUPPORT_SELECT_SOURCE | \ SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ SUPPORT_TURN_OFF | SUPPORT_TURN_ON diff --git a/homeassistant/components/opensensemap/air_quality.py b/homeassistant/components/opensensemap/air_quality.py index 5407f65a1d822f..3f859724fc32d6 100644 --- a/homeassistant/components/opensensemap/air_quality.py +++ b/homeassistant/components/opensensemap/air_quality.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle -REQUIREMENTS = ['opensensemap-api==0.1.5'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Data provided by openSenseMap' diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 1476363c6bd877..829344fb1f0804 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyotgw==0.4b3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'opentherm_gw' diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index d0b60a257705a6..bf342cc9813b87 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -14,8 +14,6 @@ DEVICE_CLASS_HEAT = 'heat' DEVICE_CLASS_PROBLEM = 'problem' -DEPENDENCIES = ['opentherm_gw'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 60f1901d43e96d..2dbd7f3cf799ef 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['opentherm_gw'] - SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index 5c64b8ab719b02..60ccedfd45134a 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -16,8 +16,6 @@ UNIT_L_MIN = 'L/min' UNIT_PERCENT = '%' -DEPENDENCIES = ['opentherm_gw'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/openuv/__init__.py b/homeassistant/components/openuv/__init__.py index 5533beb2faed4b..8e8401bbeac36c 100644 --- a/homeassistant/components/openuv/__init__.py +++ b/homeassistant/components/openuv/__init__.py @@ -15,8 +15,6 @@ from .config_flow import configured_instances from .const import DOMAIN -REQUIREMENTS = ['pyopenuv==1.0.9'] - _LOGGER = logging.getLogger(__name__) DATA_OPENUV_CLIENT = 'data_client' diff --git a/homeassistant/components/openuv/binary_sensor.py b/homeassistant/components/openuv/binary_sensor.py index cfc82a7572954d..d02312f07f8e88 100644 --- a/homeassistant/components/openuv/binary_sensor.py +++ b/homeassistant/components/openuv/binary_sensor.py @@ -16,8 +16,6 @@ ATTR_PROTECTION_WINDOW_STARTING_TIME = 'start_time' ATTR_PROTECTION_WINDOW_STARTING_UV = 'start_uv' -DEPENDENCIES = ['openuv'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/openuv/sensor.py b/homeassistant/components/openuv/sensor.py index 42780d57b3c252..2fa2e44c98ef6b 100644 --- a/homeassistant/components/openuv/sensor.py +++ b/homeassistant/components/openuv/sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['openuv'] - ATTR_MAX_UV_TIME = 'time' EXPOSURE_TYPE_MAP = { diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index 5de67721e3057f..97ab9984d5274e 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pyowm==2.10.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by OpenWeatherMap" diff --git a/homeassistant/components/openweathermap/weather.py b/homeassistant/components/openweathermap/weather.py index 8a37bc97575180..75755a53124590 100644 --- a/homeassistant/components/openweathermap/weather.py +++ b/homeassistant/components/openweathermap/weather.py @@ -14,8 +14,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle from homeassistant.util.pressure import convert as convert_pressure -REQUIREMENTS = ['pyowm==2.10.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Data provided by OpenWeatherMap' diff --git a/homeassistant/components/opple/light.py b/homeassistant/components/opple/light.py index 03e36dc179d43b..c3d66c5266308b 100644 --- a/homeassistant/components/opple/light.py +++ b/homeassistant/components/opple/light.py @@ -14,8 +14,6 @@ from homeassistant.util.color import \ color_temperature_mired_to_kelvin as mired_to_kelvin -REQUIREMENTS = ['pyoppleio==1.0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = "opple light" diff --git a/homeassistant/components/orvibo/switch.py b/homeassistant/components/orvibo/switch.py index c77e24446ec2a6..20b86dbf679b01 100644 --- a/homeassistant/components/orvibo/switch.py +++ b/homeassistant/components/orvibo/switch.py @@ -8,8 +8,6 @@ CONF_HOST, CONF_NAME, CONF_SWITCHES, CONF_MAC, CONF_DISCOVERY) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['orvibo==1.1.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Orvibo S20 Switch' diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index b880273fd1e916..dafab76a2dcebe 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['lightify==1.0.7.2'] - _LOGGER = logging.getLogger(__name__) CONF_ALLOW_LIGHTIFY_NODES = 'allow_lightify_nodes' diff --git a/homeassistant/components/otp/sensor.py b/homeassistant/components/otp/sensor.py index 2ac4c51998443e..0f79955db15678 100644 --- a/homeassistant/components/otp/sensor.py +++ b/homeassistant/components/otp/sensor.py @@ -10,8 +10,6 @@ from homeassistant.const import (CONF_NAME, CONF_TOKEN) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyotp==2.2.6'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'OTP Sensor' diff --git a/homeassistant/components/owlet/__init__.py b/homeassistant/components/owlet/__init__.py index b7ad7ab915240a..f19df6a3e38d85 100644 --- a/homeassistant/components/owlet/__init__.py +++ b/homeassistant/components/owlet/__init__.py @@ -11,8 +11,6 @@ SENSOR_BASE_STATION, SENSOR_HEART_RATE, SENSOR_MOVEMENT, SENSOR_OXYGEN_LEVEL) -REQUIREMENTS = ['pyowlet==1.0.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'owlet' diff --git a/homeassistant/components/owntracks/__init__.py b/homeassistant/components/owntracks/__init__.py index df6b815e4c5be9..e746cbc01fa5fc 100644 --- a/homeassistant/components/owntracks/__init__.py +++ b/homeassistant/components/owntracks/__init__.py @@ -16,13 +16,9 @@ from .config_flow import CONF_SECRET -REQUIREMENTS = ['PyNaCl==1.3.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'owntracks' -DEPENDENCIES = ['webhook'] - CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy' CONF_WAYPOINT_IMPORT = 'waypoints' CONF_WAYPOINT_WHITELIST = 'waypoint_whitelist' diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index 69ea723d84c503..999e883be197eb 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -10,8 +10,6 @@ from . import DOMAIN as OT_DOMAIN -DEPENDENCIES = ['owntracks'] - _LOGGER = logging.getLogger(__name__) HANDLERS = decorator.Registry() diff --git a/homeassistant/components/panasonic_bluray/media_player.py b/homeassistant/components/panasonic_bluray/media_player.py index ebf71135d343f7..9da5cf87e534e2 100644 --- a/homeassistant/components/panasonic_bluray/media_player.py +++ b/homeassistant/components/panasonic_bluray/media_player.py @@ -14,8 +14,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.dt import utcnow -REQUIREMENTS = ['panacotta==0.1'] - DEFAULT_NAME = "Panasonic Blu-Ray" SCAN_INTERVAL = timedelta(seconds=30) diff --git a/homeassistant/components/panasonic_viera/media_player.py b/homeassistant/components/panasonic_viera/media_player.py index 324becd0bf756d..4669d4ecac6896 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -14,8 +14,6 @@ CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['panasonic_viera==0.3.2', 'wakeonlan==1.1.6'] - _LOGGER = logging.getLogger(__name__) CONF_APP_POWER = 'app_power' diff --git a/homeassistant/components/pandora/media_player.py b/homeassistant/components/pandora/media_player.py index 32cde430d0e97b..14eb260914a2af 100644 --- a/homeassistant/components/pandora/media_player.py +++ b/homeassistant/components/pandora/media_player.py @@ -16,7 +16,6 @@ SERVICE_MEDIA_PLAY_PAUSE, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING) -REQUIREMENTS = ['pexpect==4.6.0'] _LOGGER = logging.getLogger(__name__) # SUPPORT_VOLUME_SET is close to available but we need volume up/down diff --git a/homeassistant/components/panel_custom/__init__.py b/homeassistant/components/panel_custom/__init__.py index 7fe2191f4c497c..9367f102441808 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'panel_custom' -DEPENDENCIES = ['frontend'] - CONF_COMPONENT_NAME = 'name' CONF_SIDEBAR_TITLE = 'sidebar_title' CONF_SIDEBAR_ICON = 'sidebar_icon' diff --git a/homeassistant/components/panel_iframe/__init__.py b/homeassistant/components/panel_iframe/__init__.py index 9319dfcc6adb27..f4038c82f71880 100644 --- a/homeassistant/components/panel_iframe/__init__.py +++ b/homeassistant/components/panel_iframe/__init__.py @@ -4,8 +4,6 @@ from homeassistant.const import CONF_ICON, CONF_URL import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['frontend'] - DOMAIN = 'panel_iframe' CONF_TITLE = 'title' diff --git a/homeassistant/components/pencom/switch.py b/homeassistant/components/pencom/switch.py index d2c73d70d96deb..3fc65e73770f1d 100644 --- a/homeassistant/components/pencom/switch.py +++ b/homeassistant/components/pencom/switch.py @@ -12,8 +12,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pencompy==0.0.3'] - _LOGGER = logging.getLogger(__name__) CONF_BOARDS = 'boards' diff --git a/homeassistant/components/philips_js/media_player.py b/homeassistant/components/philips_js/media_player.py index f5eddff8d138dc..859ad26a3ddd6c 100644 --- a/homeassistant/components/philips_js/media_player.py +++ b/homeassistant/components/philips_js/media_player.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.script import Script -REQUIREMENTS = ['ha-philipsjs==0.0.5'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=30) diff --git a/homeassistant/components/pi_hole/sensor.py b/homeassistant/components/pi_hole/sensor.py index 805e17ebdff418..061fb5c091f799 100644 --- a/homeassistant/components/pi_hole/sensor.py +++ b/homeassistant/components/pi_hole/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['hole==0.3.0'] - _LOGGER = logging.getLogger(__name__) ATTR_BLOCKED_DOMAINS = 'domains_blocked' diff --git a/homeassistant/components/piglow/light.py b/homeassistant/components/piglow/light.py index dc3906b20026cd..52e5c769560c65 100644 --- a/homeassistant/components/piglow/light.py +++ b/homeassistant/components/piglow/light.py @@ -11,8 +11,6 @@ from homeassistant.const import CONF_NAME import homeassistant.util.color as color_util -REQUIREMENTS = ['piglow==1.2.4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_PIGLOW = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/pilight/__init__.py b/homeassistant/components/pilight/__init__.py index 46be3b3720438b..b6f1a63d4d58fd 100644 --- a/homeassistant/components/pilight/__init__.py +++ b/homeassistant/components/pilight/__init__.py @@ -14,8 +14,6 @@ EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT, CONF_WHITELIST, CONF_PROTOCOL) -REQUIREMENTS = ['pilight==0.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_SEND_DELAY = 'send_delay' diff --git a/homeassistant/components/pilight/binary_sensor.py b/homeassistant/components/pilight/binary_sensor.py index 131a91b5fc3c0a..b9e95f76c491cd 100644 --- a/homeassistant/components/pilight/binary_sensor.py +++ b/homeassistant/components/pilight/binary_sensor.py @@ -26,8 +26,6 @@ CONF_RESET_DELAY_SEC = 'reset_delay_sec' DEFAULT_NAME = 'Pilight Binary Sensor' -DEPENDENCIES = ['pilight'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_VARIABLE): cv.string, vol.Required(CONF_PAYLOAD): vol.Schema(dict), diff --git a/homeassistant/components/pilight/sensor.py b/homeassistant/components/pilight/sensor.py index c36151c90dce26..a6be0f67f7cb55 100644 --- a/homeassistant/components/pilight/sensor.py +++ b/homeassistant/components/pilight/sensor.py @@ -15,8 +15,6 @@ CONF_VARIABLE = 'variable' DEFAULT_NAME = 'Pilight Sensor' -DEPENDENCIES = ['pilight'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_VARIABLE): cv.string, vol.Required(CONF_PAYLOAD): vol.Schema(dict), diff --git a/homeassistant/components/pilight/switch.py b/homeassistant/components/pilight/switch.py index d645d8e3013ce7..2f28e7f4d8aadf 100644 --- a/homeassistant/components/pilight/switch.py +++ b/homeassistant/components/pilight/switch.py @@ -21,8 +21,6 @@ CONF_UNITCODE = 'unitcode' CONF_ECHO = 'echo' -DEPENDENCIES = ['pilight'] - COMMAND_SCHEMA = vol.Schema({ vol.Optional(CONF_PROTOCOL): cv.string, vol.Optional('on'): cv.positive_int, diff --git a/homeassistant/components/pjlink/media_player.py b/homeassistant/components/pjlink/media_player.py index ad7bdc9e77cb20..00a4d49bd5c977 100644 --- a/homeassistant/components/pjlink/media_player.py +++ b/homeassistant/components/pjlink/media_player.py @@ -12,8 +12,6 @@ CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pypjlink2==1.2.0'] - _LOGGER = logging.getLogger(__name__) CONF_ENCODING = 'encoding' diff --git a/homeassistant/components/plant/__init__.py b/homeassistant/components/plant/__init__.py index 27324ad57a39a4..78f979892b1efb 100644 --- a/homeassistant/components/plant/__init__.py +++ b/homeassistant/components/plant/__init__.py @@ -89,8 +89,6 @@ }) DOMAIN = 'plant' -DEPENDENCIES = ['zone', 'group'] - GROUP_NAME_ALL_PLANTS = 'all plants' ENTITY_ID_ALL_PLANTS = group.ENTITY_ID_FORMAT.format('all_plants') diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index f2af6836e3be4c..9ff00ed1c23a51 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -20,8 +20,6 @@ from homeassistant.util import dt as dt_util from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['plexapi==3.0.6'] - _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index a3df6fdb41e8fd..4f46113347de37 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -11,8 +11,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['plexapi==3.0.6'] - _LOGGER = logging.getLogger(__name__) CONF_SERVER = 'server' diff --git a/homeassistant/components/plum_lightpad/__init__.py b/homeassistant/components/plum_lightpad/__init__.py index 5b99223d25aed7..b08727e7acc50a 100644 --- a/homeassistant/components/plum_lightpad/__init__.py +++ b/homeassistant/components/plum_lightpad/__init__.py @@ -10,8 +10,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['plumlightpad==0.0.11'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'plum_lightpad' diff --git a/homeassistant/components/plum_lightpad/light.py b/homeassistant/components/plum_lightpad/light.py index 233539560f4b8a..8923d3c5acc2e2 100644 --- a/homeassistant/components/plum_lightpad/light.py +++ b/homeassistant/components/plum_lightpad/light.py @@ -5,8 +5,6 @@ from . import PLUM_DATA -DEPENDENCIES = ['plum_lightpad'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/pocketcasts/sensor.py b/homeassistant/components/pocketcasts/sensor.py index f09e90120049d9..69d863cb9e9121 100644 --- a/homeassistant/components/pocketcasts/sensor.py +++ b/homeassistant/components/pocketcasts/sensor.py @@ -10,8 +10,6 @@ from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pocketcasts==0.1'] - _LOGGER = logging.getLogger(__name__) ICON = 'mdi:rss' diff --git a/homeassistant/components/point/__init__.py b/homeassistant/components/point/__init__.py index dc839756469dd3..c0b2f7acd0fcd0 100644 --- a/homeassistant/components/point/__init__.py +++ b/homeassistant/components/point/__init__.py @@ -20,12 +20,8 @@ CONF_WEBHOOK_URL, DOMAIN, EVENT_RECEIVED, POINT_DISCOVERY_NEW, SCAN_INTERVAL, SIGNAL_UPDATE_ENTITY, SIGNAL_WEBHOOK) -REQUIREMENTS = ['pypoint==1.1.1'] - _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['webhook'] - CONF_CLIENT_ID = 'client_id' CONF_CLIENT_SECRET = 'client_secret' diff --git a/homeassistant/components/pollen/sensor.py b/homeassistant/components/pollen/sensor.py index 3fc4d1fce3dd54..132155c7f65220 100644 --- a/homeassistant/components/pollen/sensor.py +++ b/homeassistant/components/pollen/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['numpy==1.16.2', 'pypollencom==2.2.3'] - _LOGGER = logging.getLogger(__name__) ATTR_ALLERGEN_AMOUNT = 'allergen_amount' diff --git a/homeassistant/components/postnl/sensor.py b/homeassistant/components/postnl/sensor.py index f9c8019cd31a00..d2380748c796e3 100644 --- a/homeassistant/components/postnl/sensor.py +++ b/homeassistant/components/postnl/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['postnl_api==1.0.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Information provided by PostNL' diff --git a/homeassistant/components/prezzibenzina/sensor.py b/homeassistant/components/prezzibenzina/sensor.py index 525de7dad2f837..9814e9463df7c3 100644 --- a/homeassistant/components/prezzibenzina/sensor.py +++ b/homeassistant/components/prezzibenzina/sensor.py @@ -10,8 +10,6 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['prezzibenzina-py==1.1.4'] - _LOGGER = logging.getLogger(__name__) ATTR_FUEL = 'fuel' diff --git a/homeassistant/components/proliphix/climate.py b/homeassistant/components/proliphix/climate.py index c165334201da2f..a6b4b3fd0f16c7 100644 --- a/homeassistant/components/proliphix/climate.py +++ b/homeassistant/components/proliphix/climate.py @@ -9,8 +9,6 @@ ATTR_TEMPERATURE) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['proliphix==0.4.1'] - ATTR_FAN = 'fan' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/prometheus/__init__.py b/homeassistant/components/prometheus/__init__.py index de0de8ae16241f..5119a5e0fdf47c 100644 --- a/homeassistant/components/prometheus/__init__.py +++ b/homeassistant/components/prometheus/__init__.py @@ -14,15 +14,11 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.temperature import fahrenheit_to_celsius -REQUIREMENTS = ['prometheus_client==0.2.0'] - _LOGGER = logging.getLogger(__name__) API_ENDPOINT = '/api/prometheus' DOMAIN = 'prometheus' -DEPENDENCIES = ['http'] - CONF_FILTER = 'filter' CONF_PROM_NAMESPACE = 'namespace' diff --git a/homeassistant/components/proximity/__init__.py b/homeassistant/components/proximity/__init__.py index 0a617bcec90116..c696c36f94c222 100644 --- a/homeassistant/components/proximity/__init__.py +++ b/homeassistant/components/proximity/__init__.py @@ -25,7 +25,6 @@ DEFAULT_NEAREST = 'not set' DEFAULT_PROXIMITY_ZONE = 'home' DEFAULT_TOLERANCE = 1 -DEPENDENCIES = ['zone', 'device_tracker'] DOMAIN = 'proximity' UNITS = ['km', 'm', 'mi', 'ft'] diff --git a/homeassistant/components/proxy/camera.py b/homeassistant/components/proxy/camera.py index fda2cdea60ef8e..7c535e65bc8f3d 100644 --- a/homeassistant/components/proxy/camera.py +++ b/homeassistant/components/proxy/camera.py @@ -12,8 +12,6 @@ from homeassistant.util.async_ import run_coroutine_threadsafe import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pillow==5.4.1'] - _LOGGER = logging.getLogger(__name__) CONF_CACHE_IMAGES = 'cache_images' diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index 191eb223707d99..22c21fcffbed51 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyps4-homeassistant==0.5.2'] - async def async_setup(hass, config): """Set up the PS4 Component.""" diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index 4dc4fa0a317089..3382cd6fe43ba4 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -17,8 +17,6 @@ from .const import DOMAIN as PS4_DOMAIN, REGIONS as deprecated_regions -DEPENDENCIES = ['ps4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_PS4 = SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \ diff --git a/homeassistant/components/push/camera.py b/homeassistant/components/push/camera.py index c0424f15898e58..c962aee91cad67 100644 --- a/homeassistant/components/push/camera.py +++ b/homeassistant/components/push/camera.py @@ -17,8 +17,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time import homeassistant.util.dt as dt_util -DEPENDENCIES = ['webhook'] - _LOGGER = logging.getLogger(__name__) CONF_BUFFER_SIZE = 'buffer' diff --git a/homeassistant/components/pushbullet/notify.py b/homeassistant/components/pushbullet/notify.py index 3fc90161ae0cd9..d1d9a6449ef2a3 100644 --- a/homeassistant/components/pushbullet/notify.py +++ b/homeassistant/components/pushbullet/notify.py @@ -11,8 +11,6 @@ ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['pushbullet.py==0.11.0'] - _LOGGER = logging.getLogger(__name__) ATTR_URL = 'url' diff --git a/homeassistant/components/pushbullet/sensor.py b/homeassistant/components/pushbullet/sensor.py index c90f952e7de0d3..50fa407620a80c 100644 --- a/homeassistant/components/pushbullet/sensor.py +++ b/homeassistant/components/pushbullet/sensor.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pushbullet.py==0.11.0'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { diff --git a/homeassistant/components/pushetta/notify.py b/homeassistant/components/pushetta/notify.py index 028b0cfd49212a..5c776523d1285d 100644 --- a/homeassistant/components/pushetta/notify.py +++ b/homeassistant/components/pushetta/notify.py @@ -10,8 +10,6 @@ ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pushetta==1.0.15'] - CONF_CHANNEL_NAME = 'channel_name' CONF_SEND_TEST_MSG = 'send_test_msg' diff --git a/homeassistant/components/pushover/notify.py b/homeassistant/components/pushover/notify.py index 39a1ce5d2f7fdb..d9be3428d59e10 100644 --- a/homeassistant/components/pushover/notify.py +++ b/homeassistant/components/pushover/notify.py @@ -10,7 +10,6 @@ ATTR_DATA, ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['python-pushover==0.3'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/python_script/__init__.py b/homeassistant/components/python_script/__init__.py index 56a82b3912054f..a6c7a87ae381f7 100644 --- a/homeassistant/components/python_script/__init__.py +++ b/homeassistant/components/python_script/__init__.py @@ -13,8 +13,6 @@ from homeassistant.util import sanitize_filename import homeassistant.util.dt as dt_util -REQUIREMENTS = ['restrictedpython==4.0b8'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'python_script' diff --git a/homeassistant/components/qbittorrent/sensor.py b/homeassistant/components/qbittorrent/sensor.py index 7e91c0ab276ccd..eb2529f0221ce8 100644 --- a/homeassistant/components/qbittorrent/sensor.py +++ b/homeassistant/components/qbittorrent/sensor.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['python-qbittorrent==0.3.1'] - _LOGGER = logging.getLogger(__name__) SENSOR_TYPE_CURRENT_STATUS = 'current_status' diff --git a/homeassistant/components/qnap/sensor.py b/homeassistant/components/qnap/sensor.py index e12f20c25b1903..34eb850e4b1f31 100644 --- a/homeassistant/components/qnap/sensor.py +++ b/homeassistant/components/qnap/sensor.py @@ -13,8 +13,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['qnapstats==0.2.7'] - _LOGGER = logging.getLogger(__name__) ATTR_DRIVE = 'Drive' diff --git a/homeassistant/components/qrcode/image_processing.py b/homeassistant/components/qrcode/image_processing.py index 46fa78cca7f94f..e5836135512ff5 100644 --- a/homeassistant/components/qrcode/image_processing.py +++ b/homeassistant/components/qrcode/image_processing.py @@ -3,8 +3,6 @@ from homeassistant.components.image_processing import ( ImageProcessingEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) -REQUIREMENTS = ['pyzbar==0.1.7', 'pillow==5.4.1'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the demo image processing platform.""" diff --git a/homeassistant/components/quantum_gateway/device_tracker.py b/homeassistant/components/quantum_gateway/device_tracker.py index 3472a4dbb97677..e91fe99b7cdbbd 100644 --- a/homeassistant/components/quantum_gateway/device_tracker.py +++ b/homeassistant/components/quantum_gateway/device_tracker.py @@ -9,8 +9,6 @@ from homeassistant.const import (CONF_HOST, CONF_PASSWORD, CONF_SSL) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['quantum-gateway==0.0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_HOST = 'myfiosgateway.com' diff --git a/homeassistant/components/qwikswitch/__init__.py b/homeassistant/components/qwikswitch/__init__.py index a64685956221fa..3940f055ff8e1d 100644 --- a/homeassistant/components/qwikswitch/__init__.py +++ b/homeassistant/components/qwikswitch/__init__.py @@ -14,8 +14,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyqwikswitch==0.93'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'qwikswitch' diff --git a/homeassistant/components/qwikswitch/binary_sensor.py b/homeassistant/components/qwikswitch/binary_sensor.py index a92c4d0b435e19..8042035b9c124e 100644 --- a/homeassistant/components/qwikswitch/binary_sensor.py +++ b/homeassistant/components/qwikswitch/binary_sensor.py @@ -6,8 +6,6 @@ from . import DOMAIN as QWIKSWITCH, QSEntity -DEPENDENCIES = [QWIKSWITCH] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/qwikswitch/light.py b/homeassistant/components/qwikswitch/light.py index cb4df24f9781df..1adcef56ffa3a7 100644 --- a/homeassistant/components/qwikswitch/light.py +++ b/homeassistant/components/qwikswitch/light.py @@ -3,8 +3,6 @@ from . import DOMAIN as QWIKSWITCH, QSToggleEntity -DEPENDENCIES = [QWIKSWITCH] - async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add lights from the main Qwikswitch component.""" diff --git a/homeassistant/components/qwikswitch/sensor.py b/homeassistant/components/qwikswitch/sensor.py index 8befce4f7e2a3f..047ec3475a575f 100644 --- a/homeassistant/components/qwikswitch/sensor.py +++ b/homeassistant/components/qwikswitch/sensor.py @@ -5,8 +5,6 @@ from . import DOMAIN as QWIKSWITCH, QSEntity -DEPENDENCIES = [QWIKSWITCH] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/qwikswitch/switch.py b/homeassistant/components/qwikswitch/switch.py index 4ee5396ae0ce3c..2d970a59a2a3a7 100644 --- a/homeassistant/components/qwikswitch/switch.py +++ b/homeassistant/components/qwikswitch/switch.py @@ -3,8 +3,6 @@ from . import DOMAIN as QWIKSWITCH, QSToggleEntity -DEPENDENCIES = [QWIKSWITCH] - async def async_setup_platform(hass, _, add_entities, discovery_info=None): """Add switches from the main Qwikswitch component.""" diff --git a/homeassistant/components/rachio/__init__.py b/homeassistant/components/rachio/__init__.py index 64a7a5af4d7412..1452fc6a506eb3 100644 --- a/homeassistant/components/rachio/__init__.py +++ b/homeassistant/components/rachio/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers import discovery, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['rachiopy==0.1.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'rachio' diff --git a/homeassistant/components/rachio/binary_sensor.py b/homeassistant/components/rachio/binary_sensor.py index ffcaeccacff455..ade930b00bc4fb 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -10,8 +10,6 @@ SIGNAL_RACHIO_CONTROLLER_UPDATE, STATUS_OFFLINE, STATUS_ONLINE, SUBTYPE_OFFLINE, SUBTYPE_ONLINE) -DEPENDENCIES = ['rachio'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 483e07e96f4ef3..1b650d7281a94d 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -13,8 +13,6 @@ SIGNAL_RACHIO_ZONE_UPDATE, SUBTYPE_SLEEP_MODE_OFF, SUBTYPE_SLEEP_MODE_ON, SUBTYPE_ZONE_COMPLETED, SUBTYPE_ZONE_STARTED, SUBTYPE_ZONE_STOPPED) -DEPENDENCIES = ['rachio'] - _LOGGER = logging.getLogger(__name__) ATTR_ZONE_SUMMARY = 'Summary' diff --git a/homeassistant/components/radiotherm/climate.py b/homeassistant/components/radiotherm/climate.py index 66dfc4cc385529..57cbfc031d7f9a 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -14,8 +14,6 @@ STATE_OFF) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['radiotherm==2.0.0'] - _LOGGER = logging.getLogger(__name__) ATTR_FAN = 'fan' diff --git a/homeassistant/components/rainbird/__init__.py b/homeassistant/components/rainbird/__init__.py index de0f42fda4a6b8..410fdcd92739aa 100644 --- a/homeassistant/components/rainbird/__init__.py +++ b/homeassistant/components/rainbird/__init__.py @@ -6,8 +6,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import (CONF_HOST, CONF_PASSWORD) -REQUIREMENTS = ['pyrainbird==0.1.6'] - _LOGGER = logging.getLogger(__name__) DATA_RAINBIRD = 'rainbird' diff --git a/homeassistant/components/rainbird/sensor.py b/homeassistant/components/rainbird/sensor.py index 0cee202ecb20c8..5fdf116af9d228 100644 --- a/homeassistant/components/rainbird/sensor.py +++ b/homeassistant/components/rainbird/sensor.py @@ -10,8 +10,6 @@ from . import DATA_RAINBIRD -DEPENDENCIES = ['rainbird'] - _LOGGER = logging.getLogger(__name__) # sensor_type [ description, unit, icon ] diff --git a/homeassistant/components/rainbird/switch.py b/homeassistant/components/rainbird/switch.py index 32c7c49ab99b7d..3ade3bdeadd54c 100644 --- a/homeassistant/components/rainbird/switch.py +++ b/homeassistant/components/rainbird/switch.py @@ -12,8 +12,6 @@ from . import DATA_RAINBIRD -DEPENDENCIES = ['rainbird'] - DOMAIN = 'rainbird' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py index c94315f673d9ec..e3b1a77cfa7a3a 100644 --- a/homeassistant/components/raincloud/__init__.py +++ b/homeassistant/components/raincloud/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['raincloudy==0.0.7'] - _LOGGER = logging.getLogger(__name__) ALLOWED_WATERING_TIME = [5, 10, 15, 30, 45, 60] diff --git a/homeassistant/components/raincloud/binary_sensor.py b/homeassistant/components/raincloud/binary_sensor.py index 6ebad7cc121819..37c6798916931d 100644 --- a/homeassistant/components/raincloud/binary_sensor.py +++ b/homeassistant/components/raincloud/binary_sensor.py @@ -10,8 +10,6 @@ from . import BINARY_SENSORS, DATA_RAINCLOUD, ICON_MAP, RainCloudEntity -DEPENDENCIES = ['raincloud'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/raincloud/sensor.py b/homeassistant/components/raincloud/sensor.py index 6774d48ae99860..cf0c11e22f6d8d 100644 --- a/homeassistant/components/raincloud/sensor.py +++ b/homeassistant/components/raincloud/sensor.py @@ -10,8 +10,6 @@ from . import DATA_RAINCLOUD, ICON_MAP, SENSORS, RainCloudEntity -DEPENDENCIES = ['raincloud'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/raincloud/switch.py b/homeassistant/components/raincloud/switch.py index 3901e1e0bd89ce..e320a956f11800 100644 --- a/homeassistant/components/raincloud/switch.py +++ b/homeassistant/components/raincloud/switch.py @@ -11,8 +11,6 @@ ALLOWED_WATERING_TIME, ATTRIBUTION, CONF_WATERING_TIME, DATA_RAINCLOUD, DEFAULT_WATERING_TIME, SWITCHES, RainCloudEntity) -DEPENDENCIES = ['raincloud'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 2ff5ddcd4aa2ea..f5875558a53ba1 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -22,8 +22,6 @@ from .const import ( DATA_CLIENT, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_SSL, DOMAIN) -REQUIREMENTS = ['regenmaschine==1.4.0'] - _LOGGER = logging.getLogger(__name__) DATA_LISTENER = 'listener' diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index 4387e6b67bec05..fcccf11e17c4f5 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -11,7 +11,6 @@ TYPE_HOURLY, TYPE_MONTH, TYPE_RAINDELAY, TYPE_RAINSENSOR, TYPE_WEEKDAY, RainMachineEntity) -DEPENDENCIES = ['rainmachine'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 1d438b8035f8e2..08dd67755bb5aa 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -8,7 +8,6 @@ DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, SENSOR_UPDATE_TOPIC, SENSORS, RainMachineEntity) -DEPENDENCIES = ['rainmachine'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index adcbe5598199f0..2023f1e8f5c3ce 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -12,8 +12,6 @@ DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, PROGRAM_UPDATE_TOPIC, ZONE_UPDATE_TOPIC, RainMachineEntity) -DEPENDENCIES = ['rainmachine'] - _LOGGER = logging.getLogger(__name__) ATTR_NEXT_RUN = 'next_run' diff --git a/homeassistant/components/raspihats/__init__.py b/homeassistant/components/raspihats/__init__.py index 622b98223aad66..3b37d48c87626e 100644 --- a/homeassistant/components/raspihats/__init__.py +++ b/homeassistant/components/raspihats/__init__.py @@ -6,8 +6,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['raspihats==2.2.3', 'smbus-cffi==0.5.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'raspihats' diff --git a/homeassistant/components/raspihats/binary_sensor.py b/homeassistant/components/raspihats/binary_sensor.py index 29fa474f781270..beaf66334c3b8f 100644 --- a/homeassistant/components/raspihats/binary_sensor.py +++ b/homeassistant/components/raspihats/binary_sensor.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['raspihats'] - DEFAULT_INVERT_LOGIC = False DEFAULT_DEVICE_CLASS = None diff --git a/homeassistant/components/raspihats/switch.py b/homeassistant/components/raspihats/switch.py index 93538682ad8341..082c8f72811a73 100644 --- a/homeassistant/components/raspihats/switch.py +++ b/homeassistant/components/raspihats/switch.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['raspihats'] - _CHANNELS_SCHEMA = vol.Schema([{ vol.Required(CONF_INDEX): cv.positive_int, vol.Required(CONF_NAME): cv.string, diff --git a/homeassistant/components/raspyrfm/switch.py b/homeassistant/components/raspyrfm/switch.py index a141721f3e5299..9c44fc850c74c3 100644 --- a/homeassistant/components/raspyrfm/switch.py +++ b/homeassistant/components/raspyrfm/switch.py @@ -9,7 +9,6 @@ DEVICE_DEFAULT_NAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['raspyrfm-client==1.2.8'] _LOGGER = logging.getLogger(__name__) CONF_GATEWAY_MANUFACTURER = 'gateway_manufacturer' diff --git a/homeassistant/components/recollect_waste/sensor.py b/homeassistant/components/recollect_waste/sensor.py index 1e3803ab866020..7112d22c00b090 100644 --- a/homeassistant/components/recollect_waste/sensor.py +++ b/homeassistant/components/recollect_waste/sensor.py @@ -8,8 +8,6 @@ from homeassistant.const import (CONF_NAME) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['recollect-waste==1.0.1'] - _LOGGER = logging.getLogger(__name__) ATTR_PICKUP_TYPES = 'pickup_types' ATTR_AREA_NAME = 'area_name' diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 0df1fa42ad4913..97654b21c6deb0 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -25,8 +25,6 @@ from .const import DATA_INSTANCE from .util import session_scope -REQUIREMENTS = ['sqlalchemy==1.3.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'recorder' diff --git a/homeassistant/components/recswitch/switch.py b/homeassistant/components/recswitch/switch.py index ed2da8022f8a1e..c43064c56749a3 100644 --- a/homeassistant/components/recswitch/switch.py +++ b/homeassistant/components/recswitch/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyrecswitch==1.0.2'] - DEFAULT_NAME = 'RecSwitch {0}' DATA_RSN = 'RSN' diff --git a/homeassistant/components/reddit/sensor.py b/homeassistant/components/reddit/sensor.py index 3ba43196551028..512fca71599444 100644 --- a/homeassistant/components/reddit/sensor.py +++ b/homeassistant/components/reddit/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['praw==6.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_CLIENT_ID = 'client_id' diff --git a/homeassistant/components/rejseplanen/sensor.py b/homeassistant/components/rejseplanen/sensor.py index 7a8cddb6179096..0c611e2c1e4459 100755 --- a/homeassistant/components/rejseplanen/sensor.py +++ b/homeassistant/components/rejseplanen/sensor.py @@ -19,7 +19,6 @@ from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['rjpl==0.3.5'] _LOGGER = logging.getLogger(__name__) ATTR_STOP_ID = 'Stop ID' diff --git a/homeassistant/components/remember_the_milk/__init__.py b/homeassistant/components/remember_the_milk/__init__.py index 82619e35a0ebc2..93f28b527ba862 100644 --- a/homeassistant/components/remember_the_milk/__init__.py +++ b/homeassistant/components/remember_the_milk/__init__.py @@ -13,8 +13,6 @@ # httplib2 is a transitive dependency from RtmAPI. If this dependency is not # set explicitly, the library does not work. -REQUIREMENTS = ['RtmAPI==0.7.0', 'httplib2==0.10.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'remember_the_milk' diff --git a/homeassistant/components/remote/__init__.py b/homeassistant/components/remote/__init__.py index de79adc9f0e6b5..f08abf5fd4a4cd 100644 --- a/homeassistant/components/remote/__init__.py +++ b/homeassistant/components/remote/__init__.py @@ -26,7 +26,6 @@ ATTR_HOLD_SECS = 'hold_secs' DOMAIN = 'remote' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) ENTITY_ID_ALL_REMOTES = group.ENTITY_ID_FORMAT.format('all_remotes') diff --git a/homeassistant/components/rflink/__init__.py b/homeassistant/components/rflink/__init__.py index 98e80580fea604..1dbfd208c64f7e 100644 --- a/homeassistant/components/rflink/__init__.py +++ b/homeassistant/components/rflink/__init__.py @@ -18,8 +18,6 @@ async_dispatcher_send, async_dispatcher_connect) from homeassistant.helpers.restore_state import RestoreEntity -REQUIREMENTS = ['rflink==0.0.37'] - _LOGGER = logging.getLogger(__name__) ATTR_EVENT = 'event' diff --git a/homeassistant/components/rflink/binary_sensor.py b/homeassistant/components/rflink/binary_sensor.py index 4e487eb6e81577..ae9f282be0aa42 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -14,8 +14,6 @@ CONF_OFF_DELAY = 'off_delay' DEFAULT_FORCE_UPDATE = False -DEPENDENCIES = ['rflink'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rflink/cover.py b/homeassistant/components/rflink/cover.py index 409d27862f9c3f..d78fe8312e72c9 100644 --- a/homeassistant/components/rflink/cover.py +++ b/homeassistant/components/rflink/cover.py @@ -13,8 +13,6 @@ CONF_GROUP, CONF_GROUP_ALIASES, CONF_NOGROUP_ALIASES, CONF_SIGNAL_REPETITIONS, DEVICE_DEFAULTS_SCHEMA, RflinkCommand) -DEPENDENCIES = ['rflink'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rflink/light.py b/homeassistant/components/rflink/light.py index 112ed4b4f51203..d3ef73a09bb2e1 100644 --- a/homeassistant/components/rflink/light.py +++ b/homeassistant/components/rflink/light.py @@ -15,8 +15,6 @@ CONF_SIGNAL_REPETITIONS, DATA_DEVICE_REGISTER, DEVICE_DEFAULTS_SCHEMA, EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, remove_deprecated) -DEPENDENCIES = ['rflink'] - _LOGGER = logging.getLogger(__name__) TYPE_DIMMABLE = 'dimmable' diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index c7498ece2416d8..1e3a18572ff1ed 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -15,8 +15,6 @@ EVENT_KEY_UNIT, SIGNAL_AVAILABILITY, SIGNAL_HANDLE_EVENT, TMP_ENTITY, RflinkDevice, remove_deprecated) -DEPENDENCIES = ['rflink'] - _LOGGER = logging.getLogger(__name__) SENSOR_ICONS = { diff --git a/homeassistant/components/rflink/switch.py b/homeassistant/components/rflink/switch.py index d5889c797f0e59..63f506cc13bc82 100644 --- a/homeassistant/components/rflink/switch.py +++ b/homeassistant/components/rflink/switch.py @@ -13,8 +13,6 @@ CONF_NOGROUP_ALIASES, CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, DEVICE_DEFAULTS_SCHEMA, SwitchableRflinkDevice, remove_deprecated) -DEPENDENCIES = ['rflink'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index 411f0538bde3db..f7e42ce43572f9 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import slugify -REQUIREMENTS = ['pyRFXtrx==0.23'] - DOMAIN = 'rfxtrx' DEFAULT_SIGNAL_REPETITIONS = 1 diff --git a/homeassistant/components/rfxtrx/binary_sensor.py b/homeassistant/components/rfxtrx/binary_sensor.py index d548897fb80ce1..ec32f12bc68e74 100644 --- a/homeassistant/components/rfxtrx/binary_sensor.py +++ b/homeassistant/components/rfxtrx/binary_sensor.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['rfxtrx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICES, default={}): { cv.string: vol.Schema({ diff --git a/homeassistant/components/rfxtrx/cover.py b/homeassistant/components/rfxtrx/cover.py index 7ac0e2aa43f912..a915c48818a598 100644 --- a/homeassistant/components/rfxtrx/cover.py +++ b/homeassistant/components/rfxtrx/cover.py @@ -10,8 +10,6 @@ CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS) -DEPENDENCIES = ['rfxtrx'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_DEVICES, default={}): { cv.string: vol.Schema({ diff --git a/homeassistant/components/rfxtrx/light.py b/homeassistant/components/rfxtrx/light.py index 3320a67214e2bd..56f0c21117d25e 100644 --- a/homeassistant/components/rfxtrx/light.py +++ b/homeassistant/components/rfxtrx/light.py @@ -13,8 +13,6 @@ CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS) -DEPENDENCIES = ['rfxtrx'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rfxtrx/sensor.py b/homeassistant/components/rfxtrx/sensor.py index cc54320cb672eb..94d836fa45cd9f 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -14,8 +14,6 @@ ATTR_DATA_TYPE, ATTR_FIRE_EVENT, CONF_AUTOMATIC_ADD, CONF_DATA_TYPE, CONF_DEVICES, CONF_FIRE_EVENT, DATA_TYPES) -DEPENDENCIES = ['rfxtrx'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/rfxtrx/switch.py b/homeassistant/components/rfxtrx/switch.py index 908c07ea745795..938b575eca0fe1 100644 --- a/homeassistant/components/rfxtrx/switch.py +++ b/homeassistant/components/rfxtrx/switch.py @@ -12,8 +12,6 @@ CONF_AUTOMATIC_ADD, CONF_DEVICES, CONF_FIRE_EVENT, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS) -DEPENDENCIES = ['rfxtrx'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 74da7a9d542909..669e91a1302646 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -7,8 +7,6 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['ring_doorbell==0.2.3'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Ring.com" diff --git a/homeassistant/components/ring/binary_sensor.py b/homeassistant/components/ring/binary_sensor.py index 79de0424d85a85..a12954f6c299f7 100644 --- a/homeassistant/components/ring/binary_sensor.py +++ b/homeassistant/components/ring/binary_sensor.py @@ -12,8 +12,6 @@ from . import ATTRIBUTION, DATA_RING, DEFAULT_ENTITY_NAMESPACE -DEPENDENCIES = ['ring'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/ring/camera.py b/homeassistant/components/ring/camera.py index 18427b9b6f9788..2a680a63b789db 100644 --- a/homeassistant/components/ring/camera.py +++ b/homeassistant/components/ring/camera.py @@ -16,8 +16,6 @@ CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' -DEPENDENCIES = ['ring', 'ffmpeg'] - FORCE_REFRESH_INTERVAL = timedelta(minutes=45) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index c9cb2f1159a67f..8b36cf70ea30cf 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -13,8 +13,6 @@ from . import ATTRIBUTION, DATA_RING, DEFAULT_ENTITY_NAMESPACE -DEPENDENCIES = ['ring'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=30) diff --git a/homeassistant/components/ripple/sensor.py b/homeassistant/components/ripple/sensor.py index 54530571c3eab4..773a6d77f54fb0 100644 --- a/homeassistant/components/ripple/sensor.py +++ b/homeassistant/components/ripple/sensor.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-ripple-api==0.0.3'] - ATTRIBUTION = "Data provided by ripple.com" DEFAULT_NAME = 'Ripple Balance' diff --git a/homeassistant/components/ritassist/device_tracker.py b/homeassistant/components/ritassist/device_tracker.py index 74bec1b871121f..69bf2454f860a0 100644 --- a/homeassistant/components/ritassist/device_tracker.py +++ b/homeassistant/components/ritassist/device_tracker.py @@ -9,8 +9,6 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers.event import track_utc_time_change -REQUIREMENTS = ['ritassist==0.9.2'] - _LOGGER = logging.getLogger(__name__) CONF_CLIENT_ID = 'client_id' diff --git a/homeassistant/components/rmvtransport/sensor.py b/homeassistant/components/rmvtransport/sensor.py index 7a3afb3f324bca..13d13281bb648f 100644 --- a/homeassistant/components/rmvtransport/sensor.py +++ b/homeassistant/components/rmvtransport/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['PyRMVtransport==0.1.3'] - _LOGGER = logging.getLogger(__name__) CONF_NEXT_DEPARTURE = 'next_departure' diff --git a/homeassistant/components/rocketchat/notify.py b/homeassistant/components/rocketchat/notify.py index e404114736a103..bbe02698c8e1a5 100644 --- a/homeassistant/components/rocketchat/notify.py +++ b/homeassistant/components/rocketchat/notify.py @@ -10,8 +10,6 @@ from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['rocketchat-API==0.6.1'] - _LOGGER = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter diff --git a/homeassistant/components/roku/__init__.py b/homeassistant/components/roku/__init__.py index 89bb1a9acb8fbd..3444f8a3183fe2 100644 --- a/homeassistant/components/roku/__init__.py +++ b/homeassistant/components/roku/__init__.py @@ -8,8 +8,6 @@ from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-roku==3.1.5'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'roku' diff --git a/homeassistant/components/roku/media_player.py b/homeassistant/components/roku/media_player.py index 3cf27af067433d..41eff531de373a 100644 --- a/homeassistant/components/roku/media_player.py +++ b/homeassistant/components/roku/media_player.py @@ -10,8 +10,6 @@ from homeassistant.const import (CONF_HOST, STATE_HOME, STATE_IDLE, STATE_PLAYING) -DEPENDENCIES = ['roku'] - DEFAULT_PORT = 8060 _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/roku/remote.py b/homeassistant/components/roku/remote.py index 5529918010cf45..59ecbe351adf0e 100644 --- a/homeassistant/components/roku/remote.py +++ b/homeassistant/components/roku/remote.py @@ -4,8 +4,6 @@ from homeassistant.components import remote from homeassistant.const import (CONF_HOST) -DEPENDENCIES = ['roku'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/roomba/vacuum.py b/homeassistant/components/roomba/vacuum.py index fadbe2a82d5254..e9f62d3bc179e0 100644 --- a/homeassistant/components/roomba/vacuum.py +++ b/homeassistant/components/roomba/vacuum.py @@ -14,8 +14,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['roombapy==1.3.1'] - _LOGGER = logging.getLogger(__name__) ATTR_BIN_FULL = 'bin_full' diff --git a/homeassistant/components/route53/__init__.py b/homeassistant/components/route53/__init__.py index 725dec8b8e54b2..43a7b75b94d8d9 100644 --- a/homeassistant/components/route53/__init__.py +++ b/homeassistant/components/route53/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['boto3==1.9.16', 'ipify==1.0.0'] - _LOGGER = logging.getLogger(__name__) CONF_ACCESS_KEY_ID = 'aws_access_key_id' diff --git a/homeassistant/components/rova/sensor.py b/homeassistant/components/rova/sensor.py index 2c2c36b12457ad..dcdee08734cf48 100644 --- a/homeassistant/components/rova/sensor.py +++ b/homeassistant/components/rova/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['rova==0.1.0'] - # Config for rova requests. CONF_ZIP_CODE = 'zip_code' CONF_HOUSE_NUMBER = 'house_number' diff --git a/homeassistant/components/rpi_gpio/__init__.py b/homeassistant/components/rpi_gpio/__init__.py index b5bd0796f160b8..e568281edb1cd4 100644 --- a/homeassistant/components/rpi_gpio/__init__.py +++ b/homeassistant/components/rpi_gpio/__init__.py @@ -4,8 +4,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['RPi.GPIO==0.6.5'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'rpi_gpio' diff --git a/homeassistant/components/rpi_gpio/binary_sensor.py b/homeassistant/components/rpi_gpio/binary_sensor.py index 559ae9584049be..d9903350aa0fc3 100644 --- a/homeassistant/components/rpi_gpio/binary_sensor.py +++ b/homeassistant/components/rpi_gpio/binary_sensor.py @@ -20,8 +20,6 @@ DEFAULT_INVERT_LOGIC = False DEFAULT_PULL_MODE = 'UP' -DEPENDENCIES = ['rpi_gpio'] - _SENSORS_SCHEMA = vol.Schema({ cv.positive_int: cv.string, }) diff --git a/homeassistant/components/rpi_gpio/cover.py b/homeassistant/components/rpi_gpio/cover.py index 403f7ec6867afa..d841dec777eccf 100644 --- a/homeassistant/components/rpi_gpio/cover.py +++ b/homeassistant/components/rpi_gpio/cover.py @@ -23,8 +23,6 @@ DEFAULT_STATE_PULL_MODE = 'UP' DEFAULT_INVERT_STATE = False DEFAULT_INVERT_RELAY = False -DEPENDENCIES = ['rpi_gpio'] - _COVERS_SCHEMA = vol.All( cv.ensure_list, [ diff --git a/homeassistant/components/rpi_gpio/switch.py b/homeassistant/components/rpi_gpio/switch.py index bdb79d03eec812..ba713e5d273845 100644 --- a/homeassistant/components/rpi_gpio/switch.py +++ b/homeassistant/components/rpi_gpio/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['rpi_gpio'] - CONF_PULL_MODE = 'pull_mode' CONF_PORTS = 'ports' CONF_INVERT_LOGIC = 'invert_logic' diff --git a/homeassistant/components/rpi_gpio_pwm/light.py b/homeassistant/components/rpi_gpio_pwm/light.py index b0b9ef1b763509..73ce2a67306444 100644 --- a/homeassistant/components/rpi_gpio_pwm/light.py +++ b/homeassistant/components/rpi_gpio_pwm/light.py @@ -11,8 +11,6 @@ import homeassistant.util.color as color_util from homeassistant.helpers.restore_state import RestoreEntity -REQUIREMENTS = ['pwmled==1.4.1'] - _LOGGER = logging.getLogger(__name__) CONF_LEDS = 'leds' diff --git a/homeassistant/components/rpi_pfio/__init__.py b/homeassistant/components/rpi_pfio/__init__.py index b096d9fe98ab7d..8341ebffceeca8 100644 --- a/homeassistant/components/rpi_pfio/__init__.py +++ b/homeassistant/components/rpi_pfio/__init__.py @@ -4,8 +4,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['pifacecommon==4.2.2', 'pifacedigitalio==3.0.5'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'rpi_pfio' diff --git a/homeassistant/components/rpi_pfio/binary_sensor.py b/homeassistant/components/rpi_pfio/binary_sensor.py index 677ec3bb16f121..b298c3dc44a201 100644 --- a/homeassistant/components/rpi_pfio/binary_sensor.py +++ b/homeassistant/components/rpi_pfio/binary_sensor.py @@ -18,8 +18,6 @@ DEFAULT_INVERT_LOGIC = False DEFAULT_SETTLE_TIME = 20 -DEPENDENCIES = ['rpi_pfio'] - PORT_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_SETTLE_TIME, default=DEFAULT_SETTLE_TIME): diff --git a/homeassistant/components/rpi_pfio/switch.py b/homeassistant/components/rpi_pfio/switch.py index fc158bd666f97d..5a69ec8706f15b 100644 --- a/homeassistant/components/rpi_pfio/switch.py +++ b/homeassistant/components/rpi_pfio/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['rpi_pfio'] - ATTR_INVERT_LOGIC = 'invert_logic' CONF_PORTS = 'ports' diff --git a/homeassistant/components/rpi_rf/switch.py b/homeassistant/components/rpi_rf/switch.py index d0a2337280296d..6531e42bd23536 100644 --- a/homeassistant/components/rpi_rf/switch.py +++ b/homeassistant/components/rpi_rf/switch.py @@ -9,8 +9,6 @@ CONF_NAME, CONF_SWITCHES, EVENT_HOMEASSISTANT_STOP) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['rpi-rf==0.9.7'] - _LOGGER = logging.getLogger(__name__) CONF_CODE_OFF = 'code_off' diff --git a/homeassistant/components/rss_feed_template/__init__.py b/homeassistant/components/rss_feed_template/__init__.py index 3c93fe2ac83593..1d82d40ba723e6 100644 --- a/homeassistant/components/rss_feed_template/__init__.py +++ b/homeassistant/components/rss_feed_template/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv CONTENT_TYPE_XML = 'text/xml' -DEPENDENCIES = ['http'] - DOMAIN = 'rss_feed_template' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/russound_rio/media_player.py b/homeassistant/components/russound_rio/media_player.py index b8f9d29f5cae81..e2462a2c2b42ca 100644 --- a/homeassistant/components/russound_rio/media_player.py +++ b/homeassistant/components/russound_rio/media_player.py @@ -14,8 +14,6 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['russound_rio==0.1.4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_RUSSOUND = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \ diff --git a/homeassistant/components/russound_rnet/media_player.py b/homeassistant/components/russound_rnet/media_player.py index f489d48a9d53f4..788b5da361bd16 100644 --- a/homeassistant/components/russound_rnet/media_player.py +++ b/homeassistant/components/russound_rnet/media_player.py @@ -12,8 +12,6 @@ CONF_HOST, CONF_NAME, CONF_PORT, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['russound==0.1.9'] - _LOGGER = logging.getLogger(__name__) CONF_ZONES = 'zones' diff --git a/homeassistant/components/ruter/sensor.py b/homeassistant/components/ruter/sensor.py index f6fefc96198939..fd72d59dbea50b 100644 --- a/homeassistant/components/ruter/sensor.py +++ b/homeassistant/components/ruter/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyruter==1.1.0'] - _LOGGER = logging.getLogger(__name__) CONF_STOP_ID = 'stop_id' diff --git a/homeassistant/components/sabnzbd/__init__.py b/homeassistant/components/sabnzbd/__init__.py index d070872f85c442..4275765a1bf8b7 100644 --- a/homeassistant/components/sabnzbd/__init__.py +++ b/homeassistant/components/sabnzbd/__init__.py @@ -15,8 +15,6 @@ from homeassistant.helpers.event import async_track_time_interval from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['pysabnzbd==1.1.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'sabnzbd' diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index 4968725a4befc7..3c57a844431b69 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -6,8 +6,6 @@ from . import DATA_SABNZBD, SENSOR_TYPES, SIGNAL_SABNZBD_UPDATED -DEPENDENCIES = ['sabnzbd'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 1a2a24c3621040..05921d7e84b07f 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import dt as dt_util -REQUIREMENTS = ['samsungctl[websocket]==0.7.1', 'wakeonlan==1.1.6'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Samsung TV Remote' diff --git a/homeassistant/components/satel_integra/__init__.py b/homeassistant/components/satel_integra/__init__.py index 5f5c43d961f552..2aae9ea8dd9a10 100644 --- a/homeassistant/components/satel_integra/__init__.py +++ b/homeassistant/components/satel_integra/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['satel_integra==0.3.2'] - DEFAULT_ALARM_NAME = 'satel_integra' DEFAULT_PORT = 7094 DEFAULT_CONF_ARM_HOME_MODE = 1 diff --git a/homeassistant/components/satel_integra/alarm_control_panel.py b/homeassistant/components/satel_integra/alarm_control_panel.py index d2d9f47305100d..02e683bac5a164 100644 --- a/homeassistant/components/satel_integra/alarm_control_panel.py +++ b/homeassistant/components/satel_integra/alarm_control_panel.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['satel_integra'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/satel_integra/binary_sensor.py b/homeassistant/components/satel_integra/binary_sensor.py index 0384ff37f14f01..faef1a6f45e51f 100644 --- a/homeassistant/components/satel_integra/binary_sensor.py +++ b/homeassistant/components/satel_integra/binary_sensor.py @@ -9,8 +9,6 @@ CONF_OUTPUTS, CONF_ZONE_NAME, CONF_ZONE_TYPE, CONF_ZONES, DATA_SATEL, SIGNAL_OUTPUTS_UPDATED, SIGNAL_ZONES_UPDATED) -DEPENDENCIES = ['satel_integra'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index e576eca78e872e..a5975d4f9d0b1e 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -15,8 +15,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['beautifulsoup4==4.7.1'] - _LOGGER = logging.getLogger(__name__) CONF_ATTR = 'attribute' diff --git a/homeassistant/components/script/__init__.py b/homeassistant/components/script/__init__.py index 873a18120ac3ce..528e454c4e6e38 100644 --- a/homeassistant/components/script/__init__.py +++ b/homeassistant/components/script/__init__.py @@ -18,8 +18,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'script' -DEPENDENCIES = ['group'] - ATTR_CAN_CANCEL = 'can_cancel' ATTR_LAST_ACTION = 'last_action' ATTR_LAST_TRIGGERED = 'last_triggered' diff --git a/homeassistant/components/scsgate/__init__.py b/homeassistant/components/scsgate/__init__.py index 67421e9a46ad44..ed84bc19ebe4fb 100644 --- a/homeassistant/components/scsgate/__init__.py +++ b/homeassistant/components/scsgate/__init__.py @@ -8,8 +8,6 @@ from homeassistant.core import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['scsgate==0.1.0'] - _LOGGER = logging.getLogger(__name__) ATTR_STATE = 'state' diff --git a/homeassistant/components/scsgate/cover.py b/homeassistant/components/scsgate/cover.py index fc1c16e1ff36d5..d866b8ab80c64f 100644 --- a/homeassistant/components/scsgate/cover.py +++ b/homeassistant/components/scsgate/cover.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['scsgate'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_DEVICES): cv.schema_with_slug_keys(scsgate.SCSGATE_SCHEMA), diff --git a/homeassistant/components/scsgate/light.py b/homeassistant/components/scsgate/light.py index 87d7e02b383d1d..f894d33e867927 100644 --- a/homeassistant/components/scsgate/light.py +++ b/homeassistant/components/scsgate/light.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['scsgate'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_DEVICES): cv.schema_with_slug_keys(scsgate.SCSGATE_SCHEMA), diff --git a/homeassistant/components/scsgate/switch.py b/homeassistant/components/scsgate/switch.py index 2b2bf2de94fc5b..ad6362c9e0c40c 100644 --- a/homeassistant/components/scsgate/switch.py +++ b/homeassistant/components/scsgate/switch.py @@ -11,8 +11,6 @@ ATTR_SCENARIO_ID = 'scenario_id' -DEPENDENCIES = ['scsgate'] - CONF_TRADITIONAL = 'traditional' CONF_SCENARIO = 'scenario' diff --git a/homeassistant/components/season/sensor.py b/homeassistant/components/season/sensor.py index 7c7b1054961bf3..b31cf5083bf27e 100644 --- a/homeassistant/components/season/sensor.py +++ b/homeassistant/components/season/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity from homeassistant import util -REQUIREMENTS = ['ephem==3.7.6.0'] - _LOGGER = logging.getLogger(__name__) NORTHERN = 'northern' diff --git a/homeassistant/components/sendgrid/notify.py b/homeassistant/components/sendgrid/notify.py index a717c7f24edb0d..563e47b8afe60e 100644 --- a/homeassistant/components/sendgrid/notify.py +++ b/homeassistant/components/sendgrid/notify.py @@ -10,8 +10,6 @@ from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['sendgrid==5.6.0'] - _LOGGER = logging.getLogger(__name__) CONF_SENDER_NAME = 'sender_name' diff --git a/homeassistant/components/sense/__init__.py b/homeassistant/components/sense/__init__.py index be3ab75b5553cd..7266b2fb1e5ab2 100644 --- a/homeassistant/components/sense/__init__.py +++ b/homeassistant/components/sense/__init__.py @@ -7,8 +7,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['sense_energy==0.7.0'] - _LOGGER = logging.getLogger(__name__) ACTIVE_UPDATE_RATE = 60 diff --git a/homeassistant/components/sense/binary_sensor.py b/homeassistant/components/sense/binary_sensor.py index da9bae3cc84788..a0f65ac555a585 100644 --- a/homeassistant/components/sense/binary_sensor.py +++ b/homeassistant/components/sense/binary_sensor.py @@ -5,8 +5,6 @@ from . import SENSE_DATA -DEPENDENCIES = ['sense'] - _LOGGER = logging.getLogger(__name__) BIN_SENSOR_CLASS = 'power' diff --git a/homeassistant/components/sense/sensor.py b/homeassistant/components/sense/sensor.py index 0224884e18a5cd..e114132e0d7770 100644 --- a/homeassistant/components/sense/sensor.py +++ b/homeassistant/components/sense/sensor.py @@ -15,8 +15,6 @@ CONSUMPTION_NAME = 'Usage' -DEPENDENCIES = ['sense'] - ICON = 'mdi:flash' MIN_TIME_BETWEEN_DAILY_UPDATES = timedelta(seconds=300) diff --git a/homeassistant/components/sensehat/light.py b/homeassistant/components/sensehat/light.py index c68e77b40a4d8d..713dd1373fdfc5 100644 --- a/homeassistant/components/sensehat/light.py +++ b/homeassistant/components/sensehat/light.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_NAME import homeassistant.util.color as color_util -REQUIREMENTS = ['sense-hat==2.2.0'] - _LOGGER = logging.getLogger(__name__) SUPPORT_SENSEHAT = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/sensehat/sensor.py b/homeassistant/components/sensehat/sensor.py index 870150c1a987cd..a9a80ee22bacdf 100644 --- a/homeassistant/components/sensehat/sensor.py +++ b/homeassistant/components/sensehat/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['sense-hat==2.2.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'sensehat' diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index bf06f232427a3f..d2f95dcee79318 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -21,8 +21,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util.temperature import convert as convert_temperature -REQUIREMENTS = ['pysensibo==1.0.3'] - _LOGGER = logging.getLogger(__name__) ALL = ['all'] diff --git a/homeassistant/components/serial/sensor.py b/homeassistant/components/serial/sensor.py index c01981f90218a7..cebe9b42996dab 100644 --- a/homeassistant/components/serial/sensor.py +++ b/homeassistant/components/serial/sensor.py @@ -10,8 +10,6 @@ CONF_NAME, CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyserial-asyncio==0.4'] - _LOGGER = logging.getLogger(__name__) CONF_SERIAL_PORT = 'serial_port' diff --git a/homeassistant/components/serial_pm/sensor.py b/homeassistant/components/serial_pm/sensor.py index 9ad65f7256fa25..49e61bead056c3 100644 --- a/homeassistant/components/serial_pm/sensor.py +++ b/homeassistant/components/serial_pm/sensor.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -REQUIREMENTS = ['pmsensor==0.4'] - _LOGGER = logging.getLogger(__name__) CONF_SERIAL_DEVICE = 'serial_device' diff --git a/homeassistant/components/sesame/lock.py b/homeassistant/components/sesame/lock.py index 263914f389cced..3882d8796c7099 100644 --- a/homeassistant/components/sesame/lock.py +++ b/homeassistant/components/sesame/lock.py @@ -9,8 +9,6 @@ STATE_LOCKED, STATE_UNLOCKED) from homeassistant.helpers.typing import ConfigType -REQUIREMENTS = ['pysesame==0.1.0'] - ATTR_DEVICE_ID = 'device_id' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/seventeentrack/sensor.py b/homeassistant/components/seventeentrack/sensor.py index 8a242b7737da52..f9bae50698be79 100644 --- a/homeassistant/components/seventeentrack/sensor.py +++ b/homeassistant/components/seventeentrack/sensor.py @@ -12,7 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle, slugify -REQUIREMENTS = ['py17track==2.2.2'] _LOGGER = logging.getLogger(__name__) ATTR_DESTINATION_COUNTRY = 'destination_country' diff --git a/homeassistant/components/shiftr/__init__.py b/homeassistant/components/shiftr/__init__.py index 438bc36b1bf2de..df25365a9fb9a9 100644 --- a/homeassistant/components/shiftr/__init__.py +++ b/homeassistant/components/shiftr/__init__.py @@ -9,8 +9,6 @@ EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import state as state_helper -REQUIREMENTS = ['paho-mqtt==1.4.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'shiftr' diff --git a/homeassistant/components/shodan/sensor.py b/homeassistant/components/shodan/sensor.py index ee64eecf3fe02a..072742391b9000 100644 --- a/homeassistant/components/shodan/sensor.py +++ b/homeassistant/components/shodan/sensor.py @@ -9,8 +9,6 @@ from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['shodan==1.11.1'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Shodan" diff --git a/homeassistant/components/shopping_list/__init__.py b/homeassistant/components/shopping_list/__init__.py index 1a036f3661aa38..cfcbfdd4224477 100644 --- a/homeassistant/components/shopping_list/__init__.py +++ b/homeassistant/components/shopping_list/__init__.py @@ -18,7 +18,6 @@ ATTR_NAME = 'name' DOMAIN = 'shopping_list' -DEPENDENCIES = ['http'] _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA) EVENT = 'shopping_list_updated' diff --git a/homeassistant/components/sht31/sensor.py b/homeassistant/components/sht31/sensor.py index 613b1f8c92a16b..904a1af75c2bdc 100644 --- a/homeassistant/components/sht31/sensor.py +++ b/homeassistant/components/sht31/sensor.py @@ -16,9 +16,6 @@ from homeassistant.util import Throttle -REQUIREMENTS = ['Adafruit-GPIO==1.0.3', - 'Adafruit-SHT31==1.0.2'] - _LOGGER = logging.getLogger(__name__) CONF_I2C_ADDRESS = 'i2c_address' diff --git a/homeassistant/components/simplepush/notify.py b/homeassistant/components/simplepush/notify.py index 081351238d9d0e..38a61e439726e9 100644 --- a/homeassistant/components/simplepush/notify.py +++ b/homeassistant/components/simplepush/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['simplepush==1.1.4'] - _LOGGER = logging.getLogger(__name__) ATTR_ENCRYPTED = 'encrypted' diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 359591856a7917..36aa3ba54d98f1 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -19,8 +19,6 @@ from .config_flow import configured_instances from .const import DATA_CLIENT, DEFAULT_SCAN_INTERVAL, DOMAIN, TOPIC_UPDATE -REQUIREMENTS = ['simplisafe-python==3.4.1'] - _LOGGER = logging.getLogger(__name__) CONF_ACCOUNTS = 'accounts' diff --git a/homeassistant/components/sisyphus/__init__.py b/homeassistant/components/sisyphus/__init__.py index b1bec15d40e8ac..7cc8e3efd33a53 100644 --- a/homeassistant/components/sisyphus/__init__.py +++ b/homeassistant/components/sisyphus/__init__.py @@ -13,8 +13,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform -REQUIREMENTS = ['sisyphus-control==2.1'] - _LOGGER = logging.getLogger(__name__) DATA_SISYPHUS = 'sisyphus' diff --git a/homeassistant/components/sisyphus/light.py b/homeassistant/components/sisyphus/light.py index 182e5e78198dc1..8d882925796ad6 100644 --- a/homeassistant/components/sisyphus/light.py +++ b/homeassistant/components/sisyphus/light.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['sisyphus'] - SUPPORTED_FEATURES = SUPPORT_BRIGHTNESS diff --git a/homeassistant/components/sisyphus/media_player.py b/homeassistant/components/sisyphus/media_player.py index 11546c3fd43822..65f5cb48e59b78 100644 --- a/homeassistant/components/sisyphus/media_player.py +++ b/homeassistant/components/sisyphus/media_player.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['sisyphus'] - MEDIA_TYPE_TRACK = 'sisyphus_track' SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE \ diff --git a/homeassistant/components/skybeacon/sensor.py b/homeassistant/components/skybeacon/sensor.py index 9b8b4872cdce89..aa7eea0bcccc3e 100644 --- a/homeassistant/components/skybeacon/sensor.py +++ b/homeassistant/components/skybeacon/sensor.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pygatt[GATTTOOL]==3.2.0'] - _LOGGER = logging.getLogger(__name__) ATTR_DEVICE = 'device' diff --git a/homeassistant/components/skybell/__init__.py b/homeassistant/components/skybell/__init__.py index 31d1339fbcfc88..51e12376a61d5e 100644 --- a/homeassistant/components/skybell/__init__.py +++ b/homeassistant/components/skybell/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['skybellpy==0.3.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Skybell.com" diff --git a/homeassistant/components/skybell/binary_sensor.py b/homeassistant/components/skybell/binary_sensor.py index 8c2b83552588c5..16b094dbf63089 100644 --- a/homeassistant/components/skybell/binary_sensor.py +++ b/homeassistant/components/skybell/binary_sensor.py @@ -12,8 +12,6 @@ from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice -DEPENDENCIES = ['skybell'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/skybell/camera.py b/homeassistant/components/skybell/camera.py index 04b03f84bf751d..e11f7c48947a8a 100644 --- a/homeassistant/components/skybell/camera.py +++ b/homeassistant/components/skybell/camera.py @@ -11,8 +11,6 @@ from . import DOMAIN as SKYBELL_DOMAIN, SkybellDevice -DEPENDENCIES = ['skybell'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=90) diff --git a/homeassistant/components/skybell/light.py b/homeassistant/components/skybell/light.py index d413f9df4127dc..18108787e56b43 100644 --- a/homeassistant/components/skybell/light.py +++ b/homeassistant/components/skybell/light.py @@ -7,8 +7,6 @@ from . import DOMAIN as SKYBELL_DOMAIN, SkybellDevice -DEPENDENCIES = ['skybell'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/skybell/sensor.py b/homeassistant/components/skybell/sensor.py index 067e850dfcf1dc..5188f75b6fa0fe 100644 --- a/homeassistant/components/skybell/sensor.py +++ b/homeassistant/components/skybell/sensor.py @@ -11,8 +11,6 @@ from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice -DEPENDENCIES = ['skybell'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=30) diff --git a/homeassistant/components/skybell/switch.py b/homeassistant/components/skybell/switch.py index 674bbf22a08600..7771f1a87544c1 100644 --- a/homeassistant/components/skybell/switch.py +++ b/homeassistant/components/skybell/switch.py @@ -10,8 +10,6 @@ from . import DEFAULT_ENTITY_NAMESPACE, DOMAIN as SKYBELL_DOMAIN, SkybellDevice -DEPENDENCIES = ['skybell'] - _LOGGER = logging.getLogger(__name__) # Switch types: Name diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index 026fed0a58ebb2..600fdef94776ad 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -12,8 +12,6 @@ ATTR_DATA, ATTR_TARGET, ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['slacker==0.12.0'] - _LOGGER = logging.getLogger(__name__) CONF_CHANNEL = 'default_channel' diff --git a/homeassistant/components/sleepiq/__init__.py b/homeassistant/components/sleepiq/__init__.py index 7a23c6c46095f8..97b2d53a03368b 100644 --- a/homeassistant/components/sleepiq/__init__.py +++ b/homeassistant/components/sleepiq/__init__.py @@ -13,8 +13,6 @@ DOMAIN = 'sleepiq' -REQUIREMENTS = ['sleepyq==0.6'] - MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) IS_IN_BED = 'is_in_bed' diff --git a/homeassistant/components/sleepiq/binary_sensor.py b/homeassistant/components/sleepiq/binary_sensor.py index 11f9e25d8c957e..cad5c9e42f1d60 100644 --- a/homeassistant/components/sleepiq/binary_sensor.py +++ b/homeassistant/components/sleepiq/binary_sensor.py @@ -2,8 +2,6 @@ from homeassistant.components import sleepiq from homeassistant.components.binary_sensor import BinarySensorDevice -DEPENDENCIES = ['sleepiq'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the SleepIQ sensors.""" diff --git a/homeassistant/components/sleepiq/sensor.py b/homeassistant/components/sleepiq/sensor.py index 3de444c332452d..c92c463ea24070 100644 --- a/homeassistant/components/sleepiq/sensor.py +++ b/homeassistant/components/sleepiq/sensor.py @@ -1,7 +1,6 @@ """Support for SleepIQ sensors.""" from homeassistant.components import sleepiq -DEPENDENCIES = ['sleepiq'] ICON = 'mdi:hotel' diff --git a/homeassistant/components/sma/sensor.py b/homeassistant/components/sma/sensor.py index a2ec7871f608e0..9b6406484df23c 100644 --- a/homeassistant/components/sma/sensor.py +++ b/homeassistant/components/sma/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['pysma==0.3.1'] - _LOGGER = logging.getLogger(__name__) CONF_CUSTOM = 'custom' diff --git a/homeassistant/components/smappee/__init__.py b/homeassistant/components/smappee/__init__.py index 7a495d7b89a375..c3f739b7b72961 100644 --- a/homeassistant/components/smappee/__init__.py +++ b/homeassistant/components/smappee/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.discovery import load_platform import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['smappy==0.2.16'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Smappee' diff --git a/homeassistant/components/smappee/sensor.py b/homeassistant/components/smappee/sensor.py index 98527c769d9036..e34ede56e133ca 100644 --- a/homeassistant/components/smappee/sensor.py +++ b/homeassistant/components/smappee/sensor.py @@ -7,8 +7,6 @@ from . import DATA_SMAPPEE -DEPENDENCIES = ['smappee'] - _LOGGER = logging.getLogger(__name__) SENSOR_PREFIX = 'Smappee' diff --git a/homeassistant/components/smappee/switch.py b/homeassistant/components/smappee/switch.py index 963caf457fe8db..25ae9a612ceb8f 100644 --- a/homeassistant/components/smappee/switch.py +++ b/homeassistant/components/smappee/switch.py @@ -5,8 +5,6 @@ from . import DATA_SMAPPEE -DEPENDENCIES = ['smappee'] - _LOGGER = logging.getLogger(__name__) ICON = 'mdi:power-plug' diff --git a/homeassistant/components/smartthings/__init__.py b/homeassistant/components/smartthings/__init__.py index e5226076f465e9..f2f1021ff66526 100644 --- a/homeassistant/components/smartthings/__init__.py +++ b/homeassistant/components/smartthings/__init__.py @@ -28,9 +28,6 @@ unload_smartapp_endpoint, validate_installed_app, validate_webhook_requirements) -REQUIREMENTS = ['pysmartapp==0.3.2', 'pysmartthings==0.6.7'] -DEPENDENCIES = ['webhook'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/smartthings/binary_sensor.py b/homeassistant/components/smartthings/binary_sensor.py index 45101601d5ffb2..39ff2999e3a2ff 100644 --- a/homeassistant/components/smartthings/binary_sensor.py +++ b/homeassistant/components/smartthings/binary_sensor.py @@ -6,8 +6,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - CAPABILITY_TO_ATTRIB = { 'accelerationSensor': 'acceleration', 'contactSensor': 'contact', diff --git a/homeassistant/components/smartthings/climate.py b/homeassistant/components/smartthings/climate.py index bcf2dc02cb03a2..f45ea10ce47522 100644 --- a/homeassistant/components/smartthings/climate.py +++ b/homeassistant/components/smartthings/climate.py @@ -17,8 +17,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - ATTR_OPERATION_STATE = 'operation_state' MODE_TO_STATE = { 'auto': STATE_AUTO, diff --git a/homeassistant/components/smartthings/cover.py b/homeassistant/components/smartthings/cover.py index 53602c3643c2d4..47116ad3dd6312 100644 --- a/homeassistant/components/smartthings/cover.py +++ b/homeassistant/components/smartthings/cover.py @@ -11,8 +11,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - VALUE_TO_STATE = { 'closed': STATE_CLOSED, 'closing': STATE_CLOSING, diff --git a/homeassistant/components/smartthings/fan.py b/homeassistant/components/smartthings/fan.py index e722cd21d65a61..befcb3fcb78731 100644 --- a/homeassistant/components/smartthings/fan.py +++ b/homeassistant/components/smartthings/fan.py @@ -8,8 +8,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - VALUE_TO_SPEED = { 0: SPEED_OFF, 1: SPEED_LOW, diff --git a/homeassistant/components/smartthings/light.py b/homeassistant/components/smartthings/light.py index 79a5eabc20a3cd..6e609b4b53c5a0 100644 --- a/homeassistant/components/smartthings/light.py +++ b/homeassistant/components/smartthings/light.py @@ -11,8 +11,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/smartthings/lock.py b/homeassistant/components/smartthings/lock.py index c7ab091454cad2..ca2e45114d913d 100644 --- a/homeassistant/components/smartthings/lock.py +++ b/homeassistant/components/smartthings/lock.py @@ -6,8 +6,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - ST_STATE_LOCKED = 'locked' ST_LOCK_ATTR_MAP = { 'codeId': 'code_id', diff --git a/homeassistant/components/smartthings/scene.py b/homeassistant/components/smartthings/scene.py index 9bf3211d8e36e2..17c7bd51b413f8 100644 --- a/homeassistant/components/smartthings/scene.py +++ b/homeassistant/components/smartthings/scene.py @@ -3,8 +3,6 @@ from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/smartthings/sensor.py b/homeassistant/components/smartthings/sensor.py index 4f7ad1a1398ae4..4abb3e20c3eefb 100644 --- a/homeassistant/components/smartthings/sensor.py +++ b/homeassistant/components/smartthings/sensor.py @@ -10,8 +10,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - Map = namedtuple("map", "attribute name default_unit device_class") CAPABILITY_TO_SENSORS = { diff --git a/homeassistant/components/smartthings/switch.py b/homeassistant/components/smartthings/switch.py index d30aa3a2303e9e..2149a87250e09a 100644 --- a/homeassistant/components/smartthings/switch.py +++ b/homeassistant/components/smartthings/switch.py @@ -6,8 +6,6 @@ from . import SmartThingsEntity from .const import DATA_BROKERS, DOMAIN -DEPENDENCIES = ['smartthings'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/smhi/__init__.py b/homeassistant/components/smhi/__init__.py index 608ee9b6a6d171..b62f110b619912 100644 --- a/homeassistant/components/smhi/__init__.py +++ b/homeassistant/components/smhi/__init__.py @@ -6,8 +6,6 @@ from .config_flow import smhi_locations # noqa: F401 from .const import DOMAIN # noqa: F401 -REQUIREMENTS = ['smhi-pkg==1.0.10'] - DEFAULT_NAME = 'smhi' diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index fc3399f755ccc5..ab5d08e770b475 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -19,8 +19,6 @@ from .const import ATTR_SMHI_CLOUDINESS, ENTITY_ID_SENSOR_FORMAT -DEPENDENCIES = ['smhi'] - _LOGGER = logging.getLogger(__name__) # Used to map condition from API results diff --git a/homeassistant/components/snapcast/media_player.py b/homeassistant/components/snapcast/media_player.py index b1589c4db51ab3..12ecabd68eaaec 100644 --- a/homeassistant/components/snapcast/media_player.py +++ b/homeassistant/components/snapcast/media_player.py @@ -14,8 +14,6 @@ STATE_PLAYING, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['snapcast==2.0.9'] - _LOGGER = logging.getLogger(__name__) DATA_KEY = 'snapcast' diff --git a/homeassistant/components/snips/__init__.py b/homeassistant/components/snips/__init__.py index 0cc96d66b1a1aa..71e37fa5137f11 100644 --- a/homeassistant/components/snips/__init__.py +++ b/homeassistant/components/snips/__init__.py @@ -10,8 +10,6 @@ from homeassistant.components import mqtt DOMAIN = 'snips' -DEPENDENCIES = ['mqtt'] - CONF_INTENTS = 'intents' CONF_ACTION = 'action' CONF_FEEDBACK = 'feedback_sounds' diff --git a/homeassistant/components/snmp/device_tracker.py b/homeassistant/components/snmp/device_tracker.py index 8a0fe7c6101c98..b36681161cb773 100644 --- a/homeassistant/components/snmp/device_tracker.py +++ b/homeassistant/components/snmp/device_tracker.py @@ -9,8 +9,6 @@ DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOST -REQUIREMENTS = ['pysnmp==4.4.8'] - _LOGGER = logging.getLogger(__name__) CONF_AUTHKEY = 'authkey' diff --git a/homeassistant/components/snmp/sensor.py b/homeassistant/components/snmp/sensor.py index 83d311189885ef..df132140c38bce 100644 --- a/homeassistant/components/snmp/sensor.py +++ b/homeassistant/components/snmp/sensor.py @@ -11,8 +11,6 @@ CONF_HOST, CONF_NAME, CONF_PORT, CONF_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, CONF_USERNAME, CONF_VALUE_TEMPLATE) -REQUIREMENTS = ['pysnmp==4.4.8'] - _LOGGER = logging.getLogger(__name__) CONF_BASEOID = 'baseoid' diff --git a/homeassistant/components/snmp/switch.py b/homeassistant/components/snmp/switch.py index fdb3267a3c7c32..5555f511272fd3 100644 --- a/homeassistant/components/snmp/switch.py +++ b/homeassistant/components/snmp/switch.py @@ -9,8 +9,6 @@ CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pysnmp==4.4.8'] - _LOGGER = logging.getLogger(__name__) CONF_BASEOID = 'baseoid' diff --git a/homeassistant/components/sochain/sensor.py b/homeassistant/components/sochain/sensor.py index ef6a53b7091998..3f74e43d140e36 100644 --- a/homeassistant/components/sochain/sensor.py +++ b/homeassistant/components/sochain/sensor.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-sochain-api==0.0.2'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by chain.so" diff --git a/homeassistant/components/socialblade/sensor.py b/homeassistant/components/socialblade/sensor.py index 77433ac6d57d4a..a563de83f2d6f9 100644 --- a/homeassistant/components/socialblade/sensor.py +++ b/homeassistant/components/socialblade/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['socialbladeclient==0.2'] - CHANNEL_ID = 'channel_id' DEFAULT_NAME = "Social Blade" diff --git a/homeassistant/components/solaredge/sensor.py b/homeassistant/components/solaredge/sensor.py index 6c6d7557282e2f..da0eba1320fcad 100644 --- a/homeassistant/components/solaredge/sensor.py +++ b/homeassistant/components/solaredge/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['solaredge==0.0.2', 'stringcase==1.2.0'] - # Config for solaredge monitoring api requests. CONF_SITE_ID = "site_id" diff --git a/homeassistant/components/somfy_mylink/__init__.py b/homeassistant/components/somfy_mylink/__init__.py index c8a6314acaaa50..19d41833510071 100755 --- a/homeassistant/components/somfy_mylink/__init__.py +++ b/homeassistant/components/somfy_mylink/__init__.py @@ -8,7 +8,6 @@ from homeassistant.helpers.discovery import async_load_platform _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['somfy-mylink-synergy==1.0.4'] CONF_ENTITY_CONFIG = 'entity_config' CONF_SYSTEM_ID = 'system_id' CONF_REVERSE = 'reverse' diff --git a/homeassistant/components/somfy_mylink/cover.py b/homeassistant/components/somfy_mylink/cover.py index e0b9eae36ebe73..16046d8b4111bf 100755 --- a/homeassistant/components/somfy_mylink/cover.py +++ b/homeassistant/components/somfy_mylink/cover.py @@ -7,7 +7,6 @@ from . import CONF_DEFAULT_REVERSE, DATA_SOMFY_MYLINK _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['somfy_mylink'] async def async_setup_platform(hass, diff --git a/homeassistant/components/songpal/media_player.py b/homeassistant/components/songpal/media_player.py index 842360484cfd5b..077975b26e2b6f 100644 --- a/homeassistant/components/songpal/media_player.py +++ b/homeassistant/components/songpal/media_player.py @@ -16,8 +16,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-songpal==0.0.9.1'] - _LOGGER = logging.getLogger(__name__) CONF_ENDPOINT = 'endpoint' diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index d5f89cd074ed0d..b661fa26fe7711 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -4,7 +4,6 @@ DOMAIN = 'sonos' -REQUIREMENTS = ['pysonos==0.0.10'] async def async_setup(hass, config): diff --git a/homeassistant/components/sony_projector/switch.py b/homeassistant/components/sony_projector/switch.py index 5b3ffeed75f30b..9ffd8ffb8bf623 100644 --- a/homeassistant/components/sony_projector/switch.py +++ b/homeassistant/components/sony_projector/switch.py @@ -8,8 +8,6 @@ STATE_ON, STATE_OFF, CONF_NAME, CONF_HOST) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pysdcp==1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Sony Projector' diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py index 027fad43a4013b..a2a6c315edae51 100644 --- a/homeassistant/components/soundtouch/media_player.py +++ b/homeassistant/components/soundtouch/media_player.py @@ -15,8 +15,6 @@ STATE_UNAVAILABLE) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['libsoundtouch==0.7.2'] - _LOGGER = logging.getLogger(__name__) SERVICE_PLAY_EVERYWHERE = 'soundtouch_play_everywhere' diff --git a/homeassistant/components/spaceapi/__init__.py b/homeassistant/components/spaceapi/__init__.py index fb76718f2d5f06..5431cd6260c1d8 100644 --- a/homeassistant/components/spaceapi/__init__.py +++ b/homeassistant/components/spaceapi/__init__.py @@ -45,7 +45,6 @@ CONF_TWITTER = 'twitter' DATA_SPACEAPI = 'data_spaceapi' -DEPENDENCIES = ['http'] DOMAIN = 'spaceapi' ISSUE_REPORT_CHANNELS = [CONF_EMAIL, CONF_IRC, CONF_MAILING_LIST, CONF_TWITTER] diff --git a/homeassistant/components/spc/__init__.py b/homeassistant/components/spc/__init__.py index 8aafb6f12107bd..8e06e254661ce9 100644 --- a/homeassistant/components/spc/__init__.py +++ b/homeassistant/components/spc/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyspcwebgw==0.4.0'] - _LOGGER = logging.getLogger(__name__) CONF_WS_URL = 'ws_url' diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index 48953874e8c528..838902950186ea 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -14,8 +14,6 @@ from homeassistant.helpers.event import async_track_time_interval from .const import DATA_UPDATED, DOMAIN, SENSOR_TYPES -REQUIREMENTS = ['speedtest-cli==2.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_SERVER_ID = 'server_id' diff --git a/homeassistant/components/speedtestdotnet/sensor.py b/homeassistant/components/speedtestdotnet/sensor.py index fb92bb76ac848a..785b981f1ac6fb 100644 --- a/homeassistant/components/speedtestdotnet/sensor.py +++ b/homeassistant/components/speedtestdotnet/sensor.py @@ -9,8 +9,6 @@ from .const import ( DATA_UPDATED, DOMAIN as SPEEDTESTDOTNET_DOMAIN, SENSOR_TYPES) -DEPENDENCIES = ['speedtestdotnet'] - _LOGGER = logging.getLogger(__name__) ATTR_BYTES_RECEIVED = 'bytes_received' diff --git a/homeassistant/components/spider/__init__.py b/homeassistant/components/spider/__init__.py index b565f1834577fd..aadbfc8eb9b97b 100644 --- a/homeassistant/components/spider/__init__.py +++ b/homeassistant/components/spider/__init__.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform -REQUIREMENTS = ['spiderpy==1.3.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'spider' diff --git a/homeassistant/components/spider/climate.py b/homeassistant/components/spider/climate.py index 3b612441a88466..069f34da3f7625 100644 --- a/homeassistant/components/spider/climate.py +++ b/homeassistant/components/spider/climate.py @@ -10,8 +10,6 @@ from . import DOMAIN as SPIDER_DOMAIN -DEPENDENCIES = ['spider'] - FAN_LIST = [ 'Auto', 'Low', diff --git a/homeassistant/components/spider/switch.py b/homeassistant/components/spider/switch.py index e43762be460f1d..286ea3e7ddf655 100644 --- a/homeassistant/components/spider/switch.py +++ b/homeassistant/components/spider/switch.py @@ -5,8 +5,6 @@ from . import DOMAIN as SPIDER_DOMAIN -DEPENDENCIES = ['spider'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/spotcrime/sensor.py b/homeassistant/components/spotcrime/sensor.py index fa9cfa687ec3af..a5636f543a380a 100644 --- a/homeassistant/components/spotcrime/sensor.py +++ b/homeassistant/components/spotcrime/sensor.py @@ -15,8 +15,6 @@ from homeassistant.util import slugify import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['spotcrime==1.0.3'] - _LOGGER = logging.getLogger(__name__) CONF_DAYS = 'days' diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index b9252d5035bd9f..d6014008c76aaa 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -16,8 +16,6 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['spotipy-homeassistant==2.4.4.dev1'] - _LOGGER = logging.getLogger(__name__) AUTH_CALLBACK_NAME = 'api:spotify' @@ -35,7 +33,6 @@ DEFAULT_CACHE_PATH = '.spotify-token-cache' DEFAULT_NAME = 'Spotify' -DEPENDENCIES = ['http'] DOMAIN = 'spotify' ICON = 'mdi:spotify' diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index bc40d5efb4279f..475bde97de4226 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['sqlalchemy==1.3.0'] - CONF_COLUMN_NAME = 'column' CONF_QUERIES = 'queries' CONF_QUERY = 'query' diff --git a/homeassistant/components/srp_energy/sensor.py b/homeassistant/components/srp_energy/sensor.py index 0ebae427da138a..a84295fdef905c 100644 --- a/homeassistant/components/srp_energy/sensor.py +++ b/homeassistant/components/srp_energy/sensor.py @@ -14,8 +14,6 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['srpenergy==1.0.6'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Powered by SRP Energy" diff --git a/homeassistant/components/starlingbank/sensor.py b/homeassistant/components/starlingbank/sensor.py index 00640ea49632ca..743bce5a736a48 100644 --- a/homeassistant/components/starlingbank/sensor.py +++ b/homeassistant/components/starlingbank/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['starlingbank==3.1'] - _LOGGER = logging.getLogger(__name__) BALANCE_TYPES = ['cleared_balance', 'effective_balance'] diff --git a/homeassistant/components/startca/sensor.py b/homeassistant/components/startca/sensor.py index 1e57a4cf859eba..fe2c35c39b7499 100644 --- a/homeassistant/components/startca/sensor.py +++ b/homeassistant/components/startca/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['xmltodict==0.11.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Start.ca' diff --git a/homeassistant/components/statsd/__init__.py b/homeassistant/components/statsd/__init__.py index a8c34d0a8433e1..c1b7e8de68db74 100644 --- a/homeassistant/components/statsd/__init__.py +++ b/homeassistant/components/statsd/__init__.py @@ -8,8 +8,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers import state as state_helper -REQUIREMENTS = ['statsd==3.2.1'] - _LOGGER = logging.getLogger(__name__) CONF_ATTR = 'log_attributes' diff --git a/homeassistant/components/steam_online/sensor.py b/homeassistant/components/steam_online/sensor.py index 4b4b73ad8cfd2f..1afeb2be4df2c1 100644 --- a/homeassistant/components/steam_online/sensor.py +++ b/homeassistant/components/steam_online/sensor.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['steamodd==4.21'] - _LOGGER = logging.getLogger(__name__) CONF_ACCOUNTS = 'accounts' diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 43debc504e1a15..1de1fc35ea14e4 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -19,12 +19,8 @@ from .hls import async_setup_hls from .recorder import async_setup_recorder -REQUIREMENTS = ['av==6.1.2'] - _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({}), }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/components/stride/notify.py b/homeassistant/components/stride/notify.py index fa08697d798579..1ce2cf5e221ff2 100644 --- a/homeassistant/components/stride/notify.py +++ b/homeassistant/components/stride/notify.py @@ -10,8 +10,6 @@ PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['pystride==0.1.7'] - _LOGGER = logging.getLogger(__name__) CONF_PANEL = 'panel' diff --git a/homeassistant/components/swiss_hydrological_data/sensor.py b/homeassistant/components/swiss_hydrological_data/sensor.py index 84964a94cbd141..c8a2c62c5bfddb 100644 --- a/homeassistant/components/swiss_hydrological_data/sensor.py +++ b/homeassistant/components/swiss_hydrological_data/sensor.py @@ -10,8 +10,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['swisshydrodata==0.0.3'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by the Swiss Federal Office for the " \ diff --git a/homeassistant/components/swiss_public_transport/sensor.py b/homeassistant/components/swiss_public_transport/sensor.py index 8d6b7fdee0ea85..9ff5ea71819c21 100644 --- a/homeassistant/components/swiss_public_transport/sensor.py +++ b/homeassistant/components/swiss_public_transport/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.util.dt as dt_util -REQUIREMENTS = ['python_opendata_transport==0.1.4'] - _LOGGER = logging.getLogger(__name__) ATTR_DEPARTURE_TIME1 = 'next_departure' diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 7e89a5369c8453..65b2bb7b92a4a7 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -16,7 +16,6 @@ from homeassistant.components import group DOMAIN = 'switch' -DEPENDENCIES = ['group'] SCAN_INTERVAL = timedelta(seconds=30) GROUP_NAME_ALL_SWITCHES = 'all switches' diff --git a/homeassistant/components/switchbot/switch.py b/homeassistant/components/switchbot/switch.py index 3db9b5fd2262bf..b8a2a905dcb00c 100644 --- a/homeassistant/components/switchbot/switch.py +++ b/homeassistant/components/switchbot/switch.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_NAME, CONF_MAC from homeassistant.helpers.restore_state import RestoreEntity -REQUIREMENTS = ['PySwitchbot==0.5'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Switchbot' diff --git a/homeassistant/components/switchmate/switch.py b/homeassistant/components/switchmate/switch.py index c14a6ca80879bd..ed76089147fda5 100644 --- a/homeassistant/components/switchmate/switch.py +++ b/homeassistant/components/switchmate/switch.py @@ -8,8 +8,6 @@ from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA from homeassistant.const import CONF_NAME, CONF_MAC -REQUIREMENTS = ['pySwitchmate==0.4.5'] - _LOGGER = logging.getLogger(__name__) CONF_FLIP_ON_OFF = 'flip_on_off' diff --git a/homeassistant/components/syncthru/sensor.py b/homeassistant/components/syncthru/sensor.py index 5596d4ab86adf9..33f57fa0371eaa 100644 --- a/homeassistant/components/syncthru/sensor.py +++ b/homeassistant/components/syncthru/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA -REQUIREMENTS = ['pysyncthru==0.3.1'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Samsung Printer' diff --git a/homeassistant/components/synology/camera.py b/homeassistant/components/synology/camera.py index c452f60cc2a1d2..936474652800d4 100644 --- a/homeassistant/components/synology/camera.py +++ b/homeassistant/components/synology/camera.py @@ -14,8 +14,6 @@ async_get_clientsession) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['py-synology==0.2.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Synology Camera' diff --git a/homeassistant/components/synology_srm/device_tracker.py b/homeassistant/components/synology_srm/device_tracker.py index bf5653d681bbb8..57dbb7134e207c 100644 --- a/homeassistant/components/synology_srm/device_tracker.py +++ b/homeassistant/components/synology_srm/device_tracker.py @@ -13,8 +13,6 @@ CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_PORT, CONF_SSL, CONF_VERIFY_SSL) -REQUIREMENTS = ['synology-srm==0.0.6'] - _LOGGER = logging.getLogger(__name__) DEFAULT_USERNAME = 'admin' diff --git a/homeassistant/components/synologydsm/sensor.py b/homeassistant/components/synologydsm/sensor.py index 0d5a253483f9ac..2d12dbfe763662 100644 --- a/homeassistant/components/synologydsm/sensor.py +++ b/homeassistant/components/synologydsm/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['python-synology==0.2.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = 'Data provided by Synology' diff --git a/homeassistant/components/system_health/__init__.py b/homeassistant/components/system_health/__init__.py index 9a171296ce9578..7dbb682b287b84 100644 --- a/homeassistant/components/system_health/__init__.py +++ b/homeassistant/components/system_health/__init__.py @@ -14,7 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] DOMAIN = 'system_health' INFO_CALLBACK_TIMEOUT = 5 diff --git a/homeassistant/components/system_log/__init__.py b/homeassistant/components/system_log/__init__.py index d6877c32f0deee..c5909309ab390e 100644 --- a/homeassistant/components/system_log/__init__.py +++ b/homeassistant/components/system_log/__init__.py @@ -20,7 +20,6 @@ DATA_SYSTEM_LOG = 'system_log' DEFAULT_MAX_ENTRIES = 50 DEFAULT_FIRE_EVENT = False -DEPENDENCIES = ['http'] DOMAIN = 'system_log' EVENT_SYSTEM_LOG = 'system_log_event' diff --git a/homeassistant/components/systemmonitor/sensor.py b/homeassistant/components/systemmonitor/sensor.py index cf65daa439509c..fbd4ed52de7465 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -12,8 +12,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['psutil==5.6.1'] - _LOGGER = logging.getLogger(__name__) CONF_ARG = 'arg' diff --git a/homeassistant/components/sytadin/sensor.py b/homeassistant/components/sytadin/sensor.py index 517deda7ca2b71..887d0800e332b3 100644 --- a/homeassistant/components/sytadin/sensor.py +++ b/homeassistant/components/sytadin/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['beautifulsoup4==4.7.1'] - _LOGGER = logging.getLogger(__name__) URL = 'http://www.sytadin.fr/sys/barometres_de_la_circulation.jsp.html' diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 8d3f541972e50c..9bbca925868c89 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.util import Throttle -REQUIREMENTS = ['python-tado==0.2.9'] - _LOGGER = logging.getLogger(__name__) DATA_TADO = 'tado_data' diff --git a/homeassistant/components/tahoma/__init__.py b/homeassistant/components/tahoma/__init__.py index 1807667da87dc6..9605b9e14e4e25 100644 --- a/homeassistant/components/tahoma/__init__.py +++ b/homeassistant/components/tahoma/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['tahoma-api==0.0.14'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'tahoma' diff --git a/homeassistant/components/tahoma/binary_sensor.py b/homeassistant/components/tahoma/binary_sensor.py index 948c6f90a58895..f4305077a07391 100644 --- a/homeassistant/components/tahoma/binary_sensor.py +++ b/homeassistant/components/tahoma/binary_sensor.py @@ -7,8 +7,6 @@ from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice -DEPENDENCIES = ['tahoma'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=120) diff --git a/homeassistant/components/tahoma/cover.py b/homeassistant/components/tahoma/cover.py index 85e785f9ca3b44..eeacf7c83b216c 100644 --- a/homeassistant/components/tahoma/cover.py +++ b/homeassistant/components/tahoma/cover.py @@ -7,8 +7,6 @@ from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice -DEPENDENCIES = ['tahoma'] - _LOGGER = logging.getLogger(__name__) ATTR_MEM_POS = 'memorized_position' diff --git a/homeassistant/components/tahoma/scene.py b/homeassistant/components/tahoma/scene.py index eedb95d1a772c9..cea8217b17a305 100644 --- a/homeassistant/components/tahoma/scene.py +++ b/homeassistant/components/tahoma/scene.py @@ -5,8 +5,6 @@ from . import DOMAIN as TAHOMA_DOMAIN -DEPENDENCIES = ['tahoma'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tahoma/sensor.py b/homeassistant/components/tahoma/sensor.py index 3c03911804ace1..288462dcc802e6 100644 --- a/homeassistant/components/tahoma/sensor.py +++ b/homeassistant/components/tahoma/sensor.py @@ -7,8 +7,6 @@ from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice -DEPENDENCIES = ['tahoma'] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=60) diff --git a/homeassistant/components/tahoma/switch.py b/homeassistant/components/tahoma/switch.py index 71f00ed8937c7d..4877ae61e28ff8 100644 --- a/homeassistant/components/tahoma/switch.py +++ b/homeassistant/components/tahoma/switch.py @@ -6,8 +6,6 @@ from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice -DEPENDENCIES = ['tahoma'] - _LOGGER = logging.getLogger(__name__) ATTR_RSSI_LEVEL = 'rssi_level' diff --git a/homeassistant/components/tank_utility/sensor.py b/homeassistant/components/tank_utility/sensor.py index 5389d60ef461db..8d83b0773cee05 100644 --- a/homeassistant/components/tank_utility/sensor.py +++ b/homeassistant/components/tank_utility/sensor.py @@ -12,10 +12,6 @@ from homeassistant.helpers.entity import Entity -REQUIREMENTS = [ - "tank_utility==1.4.0" -] - _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = datetime.timedelta(hours=1) diff --git a/homeassistant/components/tapsaff/binary_sensor.py b/homeassistant/components/tapsaff/binary_sensor.py index 639e9574ed971e..b2875c8e40d26f 100644 --- a/homeassistant/components/tapsaff/binary_sensor.py +++ b/homeassistant/components/tapsaff/binary_sensor.py @@ -9,8 +9,6 @@ from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['tapsaff==0.2.0'] - _LOGGER = logging.getLogger(__name__) CONF_LOCATION = 'location' diff --git a/homeassistant/components/tautulli/sensor.py b/homeassistant/components/tautulli/sensor.py index 44be10749bfa36..ca1651eca68af1 100644 --- a/homeassistant/components/tautulli/sensor.py +++ b/homeassistant/components/tautulli/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pytautulli==0.5.0'] - _LOGGER = logging.getLogger(__name__) CONF_MONITORED_USERS = 'monitored_users' diff --git a/homeassistant/components/ted5000/sensor.py b/homeassistant/components/ted5000/sensor.py index fba9866302d984..32869949eb9a0f 100644 --- a/homeassistant/components/ted5000/sensor.py +++ b/homeassistant/components/ted5000/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['xmltodict==0.11.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'ted' diff --git a/homeassistant/components/telegram/notify.py b/homeassistant/components/telegram/notify.py index 3602bbd24419f7..b18e0e2c1d1727 100644 --- a/homeassistant/components/telegram/notify.py +++ b/homeassistant/components/telegram/notify.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'telegram_bot' -DEPENDENCIES = [DOMAIN] - ATTR_KEYBOARD = 'keyboard' ATTR_INLINE_KEYBOARD = 'inline_keyboard' ATTR_PHOTO = 'photo' diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index 7d19e8212b6aca..a77b86038536e6 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -16,8 +16,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import TemplateError -REQUIREMENTS = ['python-telegram-bot==11.1.0'] - _LOGGER = logging.getLogger(__name__) ATTR_ARGS = 'args' diff --git a/homeassistant/components/telegram_bot/webhooks.py b/homeassistant/components/telegram_bot/webhooks.py index 424ece81549e2f..1a2839b176e04e 100644 --- a/homeassistant/components/telegram_bot/webhooks.py +++ b/homeassistant/components/telegram_bot/webhooks.py @@ -15,8 +15,6 @@ CONF_ALLOWED_CHAT_IDS, PLATFORM_SCHEMA, BaseTelegramBotEntity, initialize_bot) -DEPENDENCIES = ['http'] - _LOGGER = logging.getLogger(__name__) TELEGRAM_HANDLER_URL = '/api/telegram_webhooks' diff --git a/homeassistant/components/tellduslive/__init__.py b/homeassistant/components/tellduslive/__init__.py index 64f4a0102a1572..de665bc314fd7b 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -19,8 +19,6 @@ APPLICATION_NAME = 'Home Assistant' -REQUIREMENTS = ['tellduslive==0.10.10'] - _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/tellstick/__init__.py b/homeassistant/components/tellstick/__init__.py index c35d2f790273a2..815e194184bc14 100644 --- a/homeassistant/components/tellstick/__init__.py +++ b/homeassistant/components/tellstick/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['tellcore-py==1.1.2', 'tellcore-net==0.4'] - _LOGGER = logging.getLogger(__name__) ATTR_DISCOVER_CONFIG = 'config' diff --git a/homeassistant/components/tellstick/sensor.py b/homeassistant/components/tellstick/sensor.py index 0438ad79abca19..39946dac7c141c 100644 --- a/homeassistant/components/tellstick/sensor.py +++ b/homeassistant/components/tellstick/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -DEPENDENCIES = ['tellstick'] - _LOGGER = logging.getLogger(__name__) DatatypeDescription = namedtuple('DatatypeDescription', ['name', 'unit']) diff --git a/homeassistant/components/temper/sensor.py b/homeassistant/components/temper/sensor.py index 1c6cb9fdff4636..9bf6a3296fcedb 100644 --- a/homeassistant/components/temper/sensor.py +++ b/homeassistant/components/temper/sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['temperusb==1.5.3'] - CONF_SCALE = 'scale' CONF_OFFSET = 'offset' diff --git a/homeassistant/components/tensorflow/image_processing.py b/homeassistant/components/tensorflow/image_processing.py index 4e4a80a525e42e..2125ea80364fcf 100644 --- a/homeassistant/components/tensorflow/image_processing.py +++ b/homeassistant/components/tensorflow/image_processing.py @@ -12,8 +12,6 @@ from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['numpy==1.16.2', 'pillow==5.4.1', 'protobuf==3.6.1'] - _LOGGER = logging.getLogger(__name__) ATTR_MATCHES = 'matches' diff --git a/homeassistant/components/tesla/__init__.py b/homeassistant/components/tesla/__init__.py index 244538f5f462ef..894502aa50aa06 100644 --- a/homeassistant/components/tesla/__init__.py +++ b/homeassistant/components/tesla/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import slugify -REQUIREMENTS = ['teslajsonpy==0.0.25'] - DOMAIN = 'tesla' _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tesla/binary_sensor.py b/homeassistant/components/tesla/binary_sensor.py index a87239d24308d9..147853f5855a51 100644 --- a/homeassistant/components/tesla/binary_sensor.py +++ b/homeassistant/components/tesla/binary_sensor.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla binary sensor.""" diff --git a/homeassistant/components/tesla/climate.py b/homeassistant/components/tesla/climate.py index 603ce1a4d61785..cb2eee4367f39e 100644 --- a/homeassistant/components/tesla/climate.py +++ b/homeassistant/components/tesla/climate.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] - OPERATION_LIST = [STATE_ON, STATE_OFF] SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE diff --git a/homeassistant/components/tesla/device_tracker.py b/homeassistant/components/tesla/device_tracker.py index 5a7693d8370665..c3fd649ad4e1a7 100644 --- a/homeassistant/components/tesla/device_tracker.py +++ b/homeassistant/components/tesla/device_tracker.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] - def setup_scanner(hass, config, see, discovery_info=None): """Set up the Tesla tracker.""" diff --git a/homeassistant/components/tesla/lock.py b/homeassistant/components/tesla/lock.py index ade394496d6edb..4601aebf7c7548 100644 --- a/homeassistant/components/tesla/lock.py +++ b/homeassistant/components/tesla/lock.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tesla lock platform.""" diff --git a/homeassistant/components/tesla/sensor.py b/homeassistant/components/tesla/sensor.py index 99705d3f79336e..1a1fe85e25221e 100644 --- a/homeassistant/components/tesla/sensor.py +++ b/homeassistant/components/tesla/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] - SCAN_INTERVAL = timedelta(minutes=5) diff --git a/homeassistant/components/tesla/switch.py b/homeassistant/components/tesla/switch.py index e00164ff1a7d52..9b15ca092b41ad 100644 --- a/homeassistant/components/tesla/switch.py +++ b/homeassistant/components/tesla/switch.py @@ -7,7 +7,6 @@ from . import DOMAIN as TESLA_DOMAIN, TeslaDevice _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tesla'] def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/tfiac/climate.py b/homeassistant/components/tfiac/climate.py index 44fa19098236c8..c3c42b3b63bee7 100644 --- a/homeassistant/components/tfiac/climate.py +++ b/homeassistant/components/tfiac/climate.py @@ -13,8 +13,6 @@ from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, TEMP_FAHRENHEIT import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pytfiac==0.3'] - SCAN_INTERVAL = timedelta(seconds=60) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/thermoworks_smoke/sensor.py b/homeassistant/components/thermoworks_smoke/sensor.py index 0c6cddd9fcd734..55a4cd67cdd5ad 100644 --- a/homeassistant/components/thermoworks_smoke/sensor.py +++ b/homeassistant/components/thermoworks_smoke/sensor.py @@ -17,8 +17,6 @@ CONF_MONITORED_CONDITIONS, CONF_EXCLUDE, ATTR_BATTERY_LEVEL from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['thermoworks_smoke==0.1.8', 'stringcase==1.2.0'] - _LOGGER = logging.getLogger(__name__) PROBE_1 = 'probe1' diff --git a/homeassistant/components/thethingsnetwork/sensor.py b/homeassistant/components/thethingsnetwork/sensor.py index d59b429721b562..08cdecf8569522 100644 --- a/homeassistant/components/thethingsnetwork/sensor.py +++ b/homeassistant/components/thethingsnetwork/sensor.py @@ -22,8 +22,6 @@ ATTR_TIME = 'time' DEFAULT_TIMEOUT = 10 -DEPENDENCIES = ['thethingsnetwork'] - CONF_DEVICE_ID = 'device_id' CONF_VALUES = 'values' diff --git a/homeassistant/components/thingspeak/__init__.py b/homeassistant/components/thingspeak/__init__.py index 0fa15e7efb4bf4..d6191dbd3005df 100644 --- a/homeassistant/components/thingspeak/__init__.py +++ b/homeassistant/components/thingspeak/__init__.py @@ -9,8 +9,6 @@ from homeassistant.helpers import event, state as state_helper import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['thingspeak==0.4.1'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'thingspeak' diff --git a/homeassistant/components/thinkingcleaner/sensor.py b/homeassistant/components/thinkingcleaner/sensor.py index f8462435a451b6..4f05f142568450 100644 --- a/homeassistant/components/thinkingcleaner/sensor.py +++ b/homeassistant/components/thinkingcleaner/sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pythinkingcleaner==0.0.3'] - MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) diff --git a/homeassistant/components/thinkingcleaner/switch.py b/homeassistant/components/thinkingcleaner/switch.py index 38a96eb029865e..43b5a8ca422a06 100644 --- a/homeassistant/components/thinkingcleaner/switch.py +++ b/homeassistant/components/thinkingcleaner/switch.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pythinkingcleaner==0.0.3'] - MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) diff --git a/homeassistant/components/tibber/__init__.py b/homeassistant/components/tibber/__init__.py index 19cf6fe65258ea..15c684b72da4c6 100644 --- a/homeassistant/components/tibber/__init__.py +++ b/homeassistant/components/tibber/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession -REQUIREMENTS = ['pyTibber==0.10.1'] - DOMAIN = 'tibber' CONFIG_SCHEMA = vol.Schema({ diff --git a/homeassistant/components/tikteck/light.py b/homeassistant/components/tikteck/light.py index 4f5596c71be4d7..d69672cb5feaf5 100644 --- a/homeassistant/components/tikteck/light.py +++ b/homeassistant/components/tikteck/light.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['tikteck==0.4'] - _LOGGER = logging.getLogger(__name__) SUPPORT_TIKTECK_LED = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/tile/device_tracker.py b/homeassistant/components/tile/device_tracker.py index c471c1e23b4d49..f83e4bccea4d3b 100644 --- a/homeassistant/components/tile/device_tracker.py +++ b/homeassistant/components/tile/device_tracker.py @@ -13,8 +13,6 @@ from homeassistant.util.json import load_json, save_json _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pytile==2.0.6'] - CLIENT_UUID_CONFIG_FILE = '.tile.conf' DEVICE_TYPES = ['PHONE', 'TILE'] diff --git a/homeassistant/components/todoist/calendar.py b/homeassistant/components/todoist/calendar.py index 313935e1221fe8..2ee88080924f2a 100644 --- a/homeassistant/components/todoist/calendar.py +++ b/homeassistant/components/todoist/calendar.py @@ -12,8 +12,6 @@ from homeassistant.helpers.template import DATE_STR_FORMAT from homeassistant.util import Throttle, dt -REQUIREMENTS = ['todoist-python==7.0.17'] - _LOGGER = logging.getLogger(__name__) CONF_EXTRA_PROJECTS = 'custom_projects' diff --git a/homeassistant/components/tof/sensor.py b/homeassistant/components/tof/sensor.py index a403db036825c8..66b86da301c1d6 100644 --- a/homeassistant/components/tof/sensor.py +++ b/homeassistant/components/tof/sensor.py @@ -12,10 +12,6 @@ from homeassistant.const import CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['VL53L1X2==0.1.5'] - -DEPENDENCIES = ['rpi_gpio'] - _LOGGER = logging.getLogger(__name__) LENGTH_MILLIMETERS = 'mm' diff --git a/homeassistant/components/toon/__init__.py b/homeassistant/components/toon/__init__.py index d718b5895e4520..da47285934cca9 100644 --- a/homeassistant/components/toon/__init__.py +++ b/homeassistant/components/toon/__init__.py @@ -16,8 +16,6 @@ CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DISPLAY, CONF_TENANT, DATA_TOON_CLIENT, DATA_TOON_CONFIG, DOMAIN) -REQUIREMENTS = ['toonapilib==3.2.2'] - _LOGGER = logging.getLogger(__name__) # Validation of the user's configuration diff --git a/homeassistant/components/toon/binary_sensor.py b/homeassistant/components/toon/binary_sensor.py index 694b7d1d03383b..c9bec0f3e6aee7 100644 --- a/homeassistant/components/toon/binary_sensor.py +++ b/homeassistant/components/toon/binary_sensor.py @@ -12,8 +12,6 @@ ToonBoilerModuleDeviceEntity) from .const import DATA_TOON_CLIENT, DOMAIN -DEPENDENCIES = ['toon'] - _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) diff --git a/homeassistant/components/toon/climate.py b/homeassistant/components/toon/climate.py index f09dc010c792ad..d17cc641db091e 100644 --- a/homeassistant/components/toon/climate.py +++ b/homeassistant/components/toon/climate.py @@ -15,8 +15,6 @@ from . import ToonDisplayDeviceEntity from .const import DATA_TOON_CLIENT, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, DOMAIN -DEPENDENCIES = ['toon'] - _LOGGER = logging.getLogger(__name__) SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE diff --git a/homeassistant/components/toon/sensor.py b/homeassistant/components/toon/sensor.py index f58c8ef4840d66..7762aa0d822587 100644 --- a/homeassistant/components/toon/sensor.py +++ b/homeassistant/components/toon/sensor.py @@ -11,8 +11,6 @@ from .const import (CURRENCY_EUR, DATA_TOON_CLIENT, DOMAIN, POWER_KWH, POWER_WATT, VOLUME_CM3, VOLUME_M3, RATIO_PERCENT) -DEPENDENCIES = ['toon'] - _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) diff --git a/homeassistant/components/torque/sensor.py b/homeassistant/components/torque/sensor.py index 2f947c178b89ca..01efd49e862864 100644 --- a/homeassistant/components/torque/sensor.py +++ b/homeassistant/components/torque/sensor.py @@ -16,7 +16,6 @@ API_PATH = '/api/torque' DEFAULT_NAME = 'vehicle' -DEPENDENCIES = ['http'] DOMAIN = 'torque' ENTITY_NAME_FORMAT = '{0} {1}' diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index c56c4ed95a63fe..848202d6ce1445 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -13,8 +13,6 @@ STATE_ALARM_ARMED_CUSTOM_BYPASS) -REQUIREMENTS = ['total_connect_client==0.25'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Total Connect' diff --git a/homeassistant/components/touchline/climate.py b/homeassistant/components/touchline/climate.py index e003ea257d75a9..e4e4a5b7fb8b41 100644 --- a/homeassistant/components/touchline/climate.py +++ b/homeassistant/components/touchline/climate.py @@ -9,8 +9,6 @@ from homeassistant.const import CONF_HOST, TEMP_CELSIUS, ATTR_TEMPERATURE import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pytouchline==0.7'] - _LOGGER = logging.getLogger(__name__) SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE) diff --git a/homeassistant/components/tplink/__init__.py b/homeassistant/components/tplink/__init__.py index 9fc12db0d63591..2ebf342c38d510 100644 --- a/homeassistant/components/tplink/__init__.py +++ b/homeassistant/components/tplink/__init__.py @@ -33,8 +33,6 @@ }), }, extra=vol.ALLOW_EXTRA) -REQUIREMENTS = ['pyHS100==0.3.4'] - async def _async_has_devices(hass): """Return if there are devices that can be discovered.""" diff --git a/homeassistant/components/tplink/device_tracker.py b/homeassistant/components/tplink/device_tracker.py index 7f5c4a37d2451c..7b665006a44cf7 100644 --- a/homeassistant/components/tplink/device_tracker.py +++ b/homeassistant/components/tplink/device_tracker.py @@ -17,8 +17,6 @@ CONF_HOST, CONF_PASSWORD, CONF_USERNAME, HTTP_HEADER_X_REQUESTED_WITH) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['tplink==0.2.1'] - _LOGGER = logging.getLogger(__name__) HTTP_HEADER_NO_CACHE = 'no-cache' diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index 9f13766c4eff9d..6fa795bcafcad5 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -12,8 +12,6 @@ from . import CONF_LIGHT, DOMAIN as TPLINK_DOMAIN -DEPENDENCIES = ['tplink'] - PARALLEL_UPDATES = 0 _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index a4eeadd1c60a7e..3040b52cd2245b 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -9,8 +9,6 @@ from . import CONF_SWITCH, DOMAIN as TPLINK_DOMAIN -DEPENDENCIES = ['tplink'] - PARALLEL_UPDATES = 0 _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tplink_lte/__init__.py b/homeassistant/components/tplink_lte/__init__.py index ae0b73d1c7c2bf..d3d2933238d8d3 100644 --- a/homeassistant/components/tplink_lte/__init__.py +++ b/homeassistant/components/tplink_lte/__init__.py @@ -14,8 +14,6 @@ from homeassistant.helpers import config_validation as cv, discovery from homeassistant.helpers.aiohttp_client import async_create_clientsession -REQUIREMENTS = ['tp-connected==0.0.4'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'tplink_lte' diff --git a/homeassistant/components/tplink_lte/notify.py b/homeassistant/components/tplink_lte/notify.py index 519641ed34bf29..a8844979e5e172 100644 --- a/homeassistant/components/tplink_lte/notify.py +++ b/homeassistant/components/tplink_lte/notify.py @@ -9,8 +9,6 @@ from ..tplink_lte import DATA_KEY -DEPENDENCIES = ['tplink_lte'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/traccar/device_tracker.py b/homeassistant/components/traccar/device_tracker.py index 28d13dd4fe6fcc..1600227bfe2acc 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -16,8 +16,6 @@ from homeassistant.util import slugify -REQUIREMENTS = ['pytraccar==0.5.0', 'stringcase==1.2.0'] - _LOGGER = logging.getLogger(__name__) ATTR_ADDRESS = 'address' diff --git a/homeassistant/components/trackr/device_tracker.py b/homeassistant/components/trackr/device_tracker.py index 1322fde7e1ae33..55f8b7c1fafcfd 100644 --- a/homeassistant/components/trackr/device_tracker.py +++ b/homeassistant/components/trackr/device_tracker.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pytrackr==0.0.5'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string diff --git a/homeassistant/components/tradfri/__init__.py b/homeassistant/components/tradfri/__init__.py index b14bc811754350..ec339bc6312f62 100644 --- a/homeassistant/components/tradfri/__init__.py +++ b/homeassistant/components/tradfri/__init__.py @@ -13,8 +13,6 @@ from . import config_flow # noqa pylint_disable=unused-import -REQUIREMENTS = ['pytradfri[async]==6.0.1'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/tradfri/light.py b/homeassistant/components/tradfri/light.py index 07ab4806dfcc10..a2b2cdc7c49bd7 100644 --- a/homeassistant/components/tradfri/light.py +++ b/homeassistant/components/tradfri/light.py @@ -17,7 +17,6 @@ ATTR_HUE = 'hue' ATTR_SAT = 'saturation' ATTR_TRANSITION_TIME = 'transition_time' -DEPENDENCIES = ['tradfri'] PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA IKEA = 'IKEA of Sweden' TRADFRI_LIGHT_MANAGER = 'Tradfri Light Manager' diff --git a/homeassistant/components/tradfri/sensor.py b/homeassistant/components/tradfri/sensor.py index acc84a935904d3..b6f4aef370d57d 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tradfri'] - SCAN_INTERVAL = timedelta(minutes=5) diff --git a/homeassistant/components/tradfri/switch.py b/homeassistant/components/tradfri/switch.py index ef9a9537cffe18..b7826624f525c0 100644 --- a/homeassistant/components/tradfri/switch.py +++ b/homeassistant/components/tradfri/switch.py @@ -9,7 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['tradfri'] IKEA = 'IKEA of Sweden' TRADFRI_SWITCH_MANAGER = 'Tradfri Switch Manager' diff --git a/homeassistant/components/trafikverket_weatherstation/sensor.py b/homeassistant/components/trafikverket_weatherstation/sensor.py index bf8f4c803e0392..c846d020c84877 100644 --- a/homeassistant/components/trafikverket_weatherstation/sensor.py +++ b/homeassistant/components/trafikverket_weatherstation/sensor.py @@ -16,8 +16,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pytrafikverket==0.1.5.9'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Trafikverket" diff --git a/homeassistant/components/transmission/__init__.py b/homeassistant/components/transmission/__init__.py index 25e21dc3d8a090..5a2fbbff5cb8ca 100644 --- a/homeassistant/components/transmission/__init__.py +++ b/homeassistant/components/transmission/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['transmissionrpc==0.11'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'transmission' diff --git a/homeassistant/components/transmission/sensor.py b/homeassistant/components/transmission/sensor.py index dfd4c1950977b0..ac3bb3b2626722 100644 --- a/homeassistant/components/transmission/sensor.py +++ b/homeassistant/components/transmission/sensor.py @@ -9,8 +9,6 @@ from . import DATA_TRANSMISSION, DATA_UPDATED, SENSOR_TYPES -DEPENDENCIES = ['transmission'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Transmission' diff --git a/homeassistant/components/transmission/switch.py b/homeassistant/components/transmission/switch.py index 854a2e727b0b83..bd965e172b1e6d 100644 --- a/homeassistant/components/transmission/switch.py +++ b/homeassistant/components/transmission/switch.py @@ -8,8 +8,6 @@ from . import DATA_TRANSMISSION, DATA_UPDATED -DEPENDENCIES = ['transmission'] - _LOGGING = logging.getLogger(__name__) DEFAULT_NAME = 'Transmission Turtle Mode' diff --git a/homeassistant/components/transport_nsw/sensor.py b/homeassistant/components/transport_nsw/sensor.py index 3c40bf4f709e77..9549814e00213d 100644 --- a/homeassistant/components/transport_nsw/sensor.py +++ b/homeassistant/components/transport_nsw/sensor.py @@ -9,8 +9,6 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import (CONF_NAME, CONF_API_KEY, ATTR_ATTRIBUTION) -REQUIREMENTS = ['PyTransportNSW==0.1.1'] - _LOGGER = logging.getLogger(__name__) ATTR_STOP_ID = 'stop_id' diff --git a/homeassistant/components/travisci/sensor.py b/homeassistant/components/travisci/sensor.py index 99309f7e2b7533..7d94e9e910e22a 100644 --- a/homeassistant/components/travisci/sensor.py +++ b/homeassistant/components/travisci/sensor.py @@ -11,8 +11,6 @@ CONF_MONITORED_CONDITIONS) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['TravisPy==0.3.5'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Information provided by https://travis-ci.org/" diff --git a/homeassistant/components/trend/binary_sensor.py b/homeassistant/components/trend/binary_sensor.py index 163703373d3303..a7fb18bf5b7cdb 100644 --- a/homeassistant/components/trend/binary_sensor.py +++ b/homeassistant/components/trend/binary_sensor.py @@ -17,8 +17,6 @@ from homeassistant.helpers.event import async_track_state_change from homeassistant.util import utcnow -REQUIREMENTS = ['numpy==1.16.2'] - _LOGGER = logging.getLogger(__name__) ATTR_ATTRIBUTE = 'attribute' diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 763baa262bed81..ccb7989a6cfdc2 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -24,8 +24,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.setup import async_prepare_setup_platform -REQUIREMENTS = ['mutagen==1.42.0'] - _LOGGER = logging.getLogger(__name__) ATTR_CACHE = 'cache' @@ -43,7 +41,6 @@ DEFAULT_CACHE = True DEFAULT_CACHE_DIR = 'tts' DEFAULT_TIME_MEMORY = 300 -DEPENDENCIES = ['http'] DOMAIN = 'tts' MEM_CACHE_FILENAME = 'filename' diff --git a/homeassistant/components/tuya/__init__.py b/homeassistant/components/tuya/__init__.py index 117424fd55e17e..6f6b05100ecb8d 100644 --- a/homeassistant/components/tuya/__init__.py +++ b/homeassistant/components/tuya/__init__.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['tuyapy==0.1.3'] - _LOGGER = logging.getLogger(__name__) CONF_COUNTRYCODE = 'country_code' diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index b7a10dad8626c3..b6fd3be04edbbb 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -10,7 +10,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] DEVICE_TYPE = 'climate' HA_STATE_TO_TUYA = { diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 274f4d9386936f..6d43365e808c89 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -4,8 +4,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya cover devices.""" diff --git a/homeassistant/components/tuya/fan.py b/homeassistant/components/tuya/fan.py index 259417869dc2dc..897a82716afe0d 100644 --- a/homeassistant/components/tuya/fan.py +++ b/homeassistant/components/tuya/fan.py @@ -5,8 +5,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya fan platform.""" diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 17f9b43dcbebc6..cb3f82234d3858 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -6,8 +6,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya light platform.""" diff --git a/homeassistant/components/tuya/scene.py b/homeassistant/components/tuya/scene.py index 24383dca6e4c11..6a8fd9d41aa367 100644 --- a/homeassistant/components/tuya/scene.py +++ b/homeassistant/components/tuya/scene.py @@ -3,8 +3,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] - ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/tuya/switch.py b/homeassistant/components/tuya/switch.py index c2e32eedc59164..05b023a78ad053 100644 --- a/homeassistant/components/tuya/switch.py +++ b/homeassistant/components/tuya/switch.py @@ -3,8 +3,6 @@ from . import DATA_TUYA, TuyaDevice -DEPENDENCIES = ['tuya'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tuya Switch device.""" diff --git a/homeassistant/components/twilio/__init__.py b/homeassistant/components/twilio/__init__.py index e7ba06a05f7d3b..82011f499baa92 100644 --- a/homeassistant/components/twilio/__init__.py +++ b/homeassistant/components/twilio/__init__.py @@ -5,9 +5,6 @@ from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.helpers import config_entry_flow -REQUIREMENTS = ['twilio==6.19.1'] -DEPENDENCIES = ['webhook'] - DOMAIN = 'twilio' CONF_ACCOUNT_SID = 'account_sid' diff --git a/homeassistant/components/twilio_call/notify.py b/homeassistant/components/twilio_call/notify.py index ab57d7214656b1..0387ad31cb68f0 100644 --- a/homeassistant/components/twilio_call/notify.py +++ b/homeassistant/components/twilio_call/notify.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['twilio'] - CONF_FROM_NUMBER = 'from_number' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/twilio_sms/notify.py b/homeassistant/components/twilio_sms/notify.py index a04e397a5688d2..6ac6d085de5c00 100644 --- a/homeassistant/components/twilio_sms/notify.py +++ b/homeassistant/components/twilio_sms/notify.py @@ -10,8 +10,6 @@ BaseNotificationService) _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ["twilio"] - CONF_FROM_NUMBER = "from_number" diff --git a/homeassistant/components/twitch/sensor.py b/homeassistant/components/twitch/sensor.py index 123de752d51255..e5223b13b01b23 100644 --- a/homeassistant/components/twitch/sensor.py +++ b/homeassistant/components/twitch/sensor.py @@ -7,8 +7,6 @@ from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-twitch-client==0.6.0'] - _LOGGER = logging.getLogger(__name__) ATTR_GAME = 'game' diff --git a/homeassistant/components/twitter/notify.py b/homeassistant/components/twitter/notify.py index 54cd591f394575..305fec7269d49e 100644 --- a/homeassistant/components/twitter/notify.py +++ b/homeassistant/components/twitter/notify.py @@ -15,8 +15,6 @@ from homeassistant.components.notify import (ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['TwitterAPI==2.5.9'] - _LOGGER = logging.getLogger(__name__) CONF_CONSUMER_KEY = 'consumer_key' diff --git a/homeassistant/components/ubee/device_tracker.py b/homeassistant/components/ubee/device_tracker.py index f73f58f3a1f258..8e610a4f51c808 100644 --- a/homeassistant/components/ubee/device_tracker.py +++ b/homeassistant/components/ubee/device_tracker.py @@ -9,8 +9,6 @@ CONF_HOST, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyubee==0.5'] - _LOGGER = logging.getLogger(__name__) CONF_MODEL = 'model' diff --git a/homeassistant/components/uber/sensor.py b/homeassistant/components/uber/sensor.py index 87d87de66ee30e..324124ca960bfb 100644 --- a/homeassistant/components/uber/sensor.py +++ b/homeassistant/components/uber/sensor.py @@ -9,8 +9,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['uber_rides==0.6.0'] - _LOGGER = logging.getLogger(__name__) CONF_END_LATITUDE = 'end_latitude' diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index 7e236789a5c58f..3af450acdbfbc3 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -16,8 +16,6 @@ DEFAULT_SITE_ID = 'default' DEFAULT_VERIFY_SSL = False -REQUIREMENTS = ['aiounifi==4'] - async def async_setup(hass, config): """Component doesn't support configuration through configuration.yaml.""" diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 49e28114b17098..8bf384eef14f72 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_VERIFY_SSL, CONF_MONITORED_CONDITIONS import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyunifi==2.16'] - _LOGGER = logging.getLogger(__name__) CONF_PORT = 'port' CONF_SITE_ID = 'site_id' diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index e90da2dbcd85fe..5f33a9c08d35fb 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -11,9 +11,8 @@ from homeassistant.core import callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC -from .const import CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID, DOMAIN +from .const import CONF_CONTROLLER, CONF_SITE_ID, CONTROLLER_ID -DEPENDENCIES = [DOMAIN] SCAN_INTERVAL = timedelta(seconds=15) LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/unifi_direct/device_tracker.py b/homeassistant/components/unifi_direct/device_tracker.py index 29a3c58fab95ef..544314c62c5c92 100644 --- a/homeassistant/components/unifi_direct/device_tracker.py +++ b/homeassistant/components/unifi_direct/device_tracker.py @@ -11,8 +11,6 @@ CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT) -REQUIREMENTS = ['pexpect==4.6.0'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SSH_PORT = 22 diff --git a/homeassistant/components/upc_connect/device_tracker.py b/homeassistant/components/upc_connect/device_tracker.py index 4a583b8349ad15..4b4c32182bd173 100644 --- a/homeassistant/components/upc_connect/device_tracker.py +++ b/homeassistant/components/upc_connect/device_tracker.py @@ -13,8 +13,6 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['defusedxml==0.5.0'] - _LOGGER = logging.getLogger(__name__) CMD_DEVICES = 123 diff --git a/homeassistant/components/upcloud/__init__.py b/homeassistant/components/upcloud/__init__.py index 7981cf948bb9ec..ea964c9027d96a 100644 --- a/homeassistant/components/upcloud/__init__.py +++ b/homeassistant/components/upcloud/__init__.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['upcloud-api==0.4.3'] - _LOGGER = logging.getLogger(__name__) ATTR_CORE_NUMBER = 'core_number' diff --git a/homeassistant/components/upcloud/binary_sensor.py b/homeassistant/components/upcloud/binary_sensor.py index a0c3c9f34c6fdc..e959f54f25486d 100644 --- a/homeassistant/components/upcloud/binary_sensor.py +++ b/homeassistant/components/upcloud/binary_sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['upcloud'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string]), }) diff --git a/homeassistant/components/upcloud/switch.py b/homeassistant/components/upcloud/switch.py index 7e84adccf5536e..ee1c1498f98f92 100644 --- a/homeassistant/components/upcloud/switch.py +++ b/homeassistant/components/upcloud/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['upcloud'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string]), }) diff --git a/homeassistant/components/updater/__init__.py b/homeassistant/components/updater/__init__.py index cb2646ea942b56..95b1372418cea6 100644 --- a/homeassistant/components/updater/__init__.py +++ b/homeassistant/components/updater/__init__.py @@ -18,8 +18,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['distro==1.4.0'] - _LOGGER = logging.getLogger(__name__) ATTR_RELEASE_NOTES = 'release_notes' diff --git a/homeassistant/components/upnp/__init__.py b/homeassistant/components/upnp/__init__.py index 5f4abcb24c7918..01f6d6159f0e3d 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -23,8 +23,6 @@ from .const import LOGGER as _LOGGER from .device import Device -REQUIREMENTS = ['async-upnp-client==0.14.7'] - NOTIFICATION_ID = 'upnp_notification' NOTIFICATION_TITLE = 'UPnP/IGD Setup' diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index 86bcee879b9dbd..411d529b33f29b 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['upnp'] - BYTES_RECEIVED = 'bytes_received' BYTES_SENT = 'bytes_sent' PACKETS_RECEIVED = 'packets_received' diff --git a/homeassistant/components/ups/sensor.py b/homeassistant/components/ups/sensor.py index 3ed82de41dbb95..55451d4bbfda28 100644 --- a/homeassistant/components/ups/sensor.py +++ b/homeassistant/components/ups/sensor.py @@ -15,8 +15,6 @@ from homeassistant.util import Throttle, slugify from homeassistant.util.dt import now, parse_date -REQUIREMENTS = ['upsmychoice==1.0.6'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'ups' diff --git a/homeassistant/components/uptimerobot/binary_sensor.py b/homeassistant/components/uptimerobot/binary_sensor.py index 8e11966b6809d4..90b71c026dc466 100644 --- a/homeassistant/components/uptimerobot/binary_sensor.py +++ b/homeassistant/components/uptimerobot/binary_sensor.py @@ -8,8 +8,6 @@ from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyuptimerobot==0.0.5'] - _LOGGER = logging.getLogger(__name__) ATTR_TARGET = 'target' diff --git a/homeassistant/components/uscis/sensor.py b/homeassistant/components/uscis/sensor.py index 501c6c9665c909..59b37c7ea65a39 100644 --- a/homeassistant/components/uscis/sensor.py +++ b/homeassistant/components/uscis/sensor.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['uscisstatus==0.1.1'] - DEFAULT_NAME = "USCIS" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/usgs_earthquakes_feed/geo_location.py b/homeassistant/components/usgs_earthquakes_feed/geo_location.py index 1d11b1971ccaaf..60d1f6925a4c04 100644 --- a/homeassistant/components/usgs_earthquakes_feed/geo_location.py +++ b/homeassistant/components/usgs_earthquakes_feed/geo_location.py @@ -16,8 +16,6 @@ async_dispatcher_connect, dispatcher_send) from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['geojson_client==0.3'] - _LOGGER = logging.getLogger(__name__) ATTR_ALERT = 'alert' diff --git a/homeassistant/components/usps/__init__.py b/homeassistant/components/usps/__init__.py index 8a7d7d52255fd2..eb2882d2a56bf9 100644 --- a/homeassistant/components/usps/__init__.py +++ b/homeassistant/components/usps/__init__.py @@ -10,8 +10,6 @@ from homeassistant.util import Throttle from homeassistant.util.dt import now -REQUIREMENTS = ['myusps==1.3.2'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'usps' diff --git a/homeassistant/components/usps/camera.py b/homeassistant/components/usps/camera.py index 5b5eaca4ce293a..cd0a216517ba19 100644 --- a/homeassistant/components/usps/camera.py +++ b/homeassistant/components/usps/camera.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['usps'] - SCAN_INTERVAL = timedelta(seconds=10) diff --git a/homeassistant/components/usps/sensor.py b/homeassistant/components/usps/sensor.py index 3e5fea5c4ee7f0..4580978da75f83 100644 --- a/homeassistant/components/usps/sensor.py +++ b/homeassistant/components/usps/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['usps'] - STATUS_DELIVERED = 'delivered' diff --git a/homeassistant/components/uvc/camera.py b/homeassistant/components/uvc/camera.py index 65251054060484..423c27e0781a17 100644 --- a/homeassistant/components/uvc/camera.py +++ b/homeassistant/components/uvc/camera.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['uvcclient==0.11.0'] - _LOGGER = logging.getLogger(__name__) CONF_NVR = 'nvr' diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 02266986ccfbef..0e44d494b564d6 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -20,8 +20,6 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'vacuum' -DEPENDENCIES = ['group'] - SCAN_INTERVAL = timedelta(seconds=20) GROUP_NAME_ALL_VACUUMS = 'all vacuum cleaners' diff --git a/homeassistant/components/vasttrafik/sensor.py b/homeassistant/components/vasttrafik/sensor.py index d8e9f1e7675262..45279fa8933fa2 100644 --- a/homeassistant/components/vasttrafik/sensor.py +++ b/homeassistant/components/vasttrafik/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['vtjp==0.1.14'] - _LOGGER = logging.getLogger(__name__) ATTR_ACCESSIBILITY = 'accessibility' diff --git a/homeassistant/components/velbus/__init__.py b/homeassistant/components/velbus/__init__.py index 4e808dc21ca5ce..73cd0d734bdd1c 100644 --- a/homeassistant/components/velbus/__init__.py +++ b/homeassistant/components/velbus/__init__.py @@ -7,8 +7,6 @@ from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-velbus==2.0.22'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'velbus' diff --git a/homeassistant/components/velbus/binary_sensor.py b/homeassistant/components/velbus/binary_sensor.py index cbe1350bd4f667..82a1c5568fc026 100644 --- a/homeassistant/components/velbus/binary_sensor.py +++ b/homeassistant/components/velbus/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['velbus'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index 470524bb6f3dff..0471e5b87e069d 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['velbus'] - SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE) diff --git a/homeassistant/components/velbus/cover.py b/homeassistant/components/velbus/cover.py index b176ab76c4b74f..fb9cea93455646 100644 --- a/homeassistant/components/velbus/cover.py +++ b/homeassistant/components/velbus/cover.py @@ -24,8 +24,6 @@ vol.Required(CONF_COVERS): cv.schema_with_slug_keys(COVER_SCHEMA), }) -DEPENDENCIES = ['velbus'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up cover controlled by Velbus.""" diff --git a/homeassistant/components/velbus/sensor.py b/homeassistant/components/velbus/sensor.py index ad78a795a30b56..b8287aef41a1b9 100644 --- a/homeassistant/components/velbus/sensor.py +++ b/homeassistant/components/velbus/sensor.py @@ -5,8 +5,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['velbus'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/velbus/switch.py b/homeassistant/components/velbus/switch.py index b5ef89ca48031d..0835e2bd209bf0 100644 --- a/homeassistant/components/velbus/switch.py +++ b/homeassistant/components/velbus/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['velbus'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/velux/__init__.py b/homeassistant/components/velux/__init__.py index a46f62dbd5fcf8..1a1444f22aefa6 100644 --- a/homeassistant/components/velux/__init__.py +++ b/homeassistant/components/velux/__init__.py @@ -12,8 +12,6 @@ SUPPORTED_DOMAINS = ['cover', 'scene'] _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pyvlx==0.2.10'] - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_HOST): cv.string, diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index 1893909b70608c..3c1b6ecb1eb0ff 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -6,8 +6,6 @@ from . import DATA_VELUX -DEPENDENCIES = ['velux'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/velux/scene.py b/homeassistant/components/velux/scene.py index 614d3f349a2366..f33296780d7664 100644 --- a/homeassistant/components/velux/scene.py +++ b/homeassistant/components/velux/scene.py @@ -3,8 +3,6 @@ from . import _LOGGER, DATA_VELUX -DEPENDENCIES = ['velux'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/venstar/climate.py b/homeassistant/components/venstar/climate.py index f3e7542af5ce9f..68b6ff88857b2a 100644 --- a/homeassistant/components/venstar/climate.py +++ b/homeassistant/components/venstar/climate.py @@ -17,8 +17,6 @@ TEMP_FAHRENHEIT) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['venstarcolortouch==0.6'] - _LOGGER = logging.getLogger(__name__) ATTR_FAN_STATE = 'fan_state' diff --git a/homeassistant/components/vera/__init__.py b/homeassistant/components/vera/__init__.py index 3f4c66d238a89b..1c5d9f811ad13c 100644 --- a/homeassistant/components/vera/__init__.py +++ b/homeassistant/components/vera/__init__.py @@ -14,8 +14,6 @@ EVENT_HOMEASSISTANT_STOP, CONF_LIGHTS, CONF_EXCLUDE) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyvera==0.2.45'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'vera' diff --git a/homeassistant/components/vera/binary_sensor.py b/homeassistant/components/vera/binary_sensor.py index c81fa31938f554..7482e39e721e92 100644 --- a/homeassistant/components/vera/binary_sensor.py +++ b/homeassistant/components/vera/binary_sensor.py @@ -6,8 +6,6 @@ from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice -DEPENDENCIES = ['vera'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vera/climate.py b/homeassistant/components/vera/climate.py index f8ff9c21b89aae..dba074f73efafa 100644 --- a/homeassistant/components/vera/climate.py +++ b/homeassistant/components/vera/climate.py @@ -11,8 +11,6 @@ from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice -DEPENDENCIES = ['vera'] - _LOGGER = logging.getLogger(__name__) OPERATION_LIST = [STATE_HEAT, STATE_COOL, STATE_AUTO, STATE_OFF] diff --git a/homeassistant/components/vera/cover.py b/homeassistant/components/vera/cover.py index 4cf2aac3bb4e3c..ac61a913128713 100644 --- a/homeassistant/components/vera/cover.py +++ b/homeassistant/components/vera/cover.py @@ -6,8 +6,6 @@ from . import VERA_CONTROLLER, VERA_DEVICES, VeraDevice -DEPENDENCIES = ['vera'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/vera/light.py b/homeassistant/components/vera/light.py index e4e315bb52e87d..4ea9ad4400a3c7 100644 --- a/homeassistant/components/vera/light.py +++ b/homeassistant/components/vera/light.py @@ -10,8 +10,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['vera'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera lights.""" diff --git a/homeassistant/components/vera/lock.py b/homeassistant/components/vera/lock.py index 5ace07b87d7087..9ceb06d8a8658c 100644 --- a/homeassistant/components/vera/lock.py +++ b/homeassistant/components/vera/lock.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['vera'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Find and return Vera locks.""" diff --git a/homeassistant/components/vera/scene.py b/homeassistant/components/vera/scene.py index 5000f9bc50f9c8..f3659fa3e9b5fc 100644 --- a/homeassistant/components/vera/scene.py +++ b/homeassistant/components/vera/scene.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['vera'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera scenes.""" diff --git a/homeassistant/components/vera/sensor.py b/homeassistant/components/vera/sensor.py index 3c026046b3eea0..caec102eb1f68c 100644 --- a/homeassistant/components/vera/sensor.py +++ b/homeassistant/components/vera/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['vera'] - SCAN_INTERVAL = timedelta(seconds=5) diff --git a/homeassistant/components/vera/switch.py b/homeassistant/components/vera/switch.py index f422e49bf42f8e..0f7654c97201e6 100644 --- a/homeassistant/components/vera/switch.py +++ b/homeassistant/components/vera/switch.py @@ -8,8 +8,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['vera'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Vera switches.""" diff --git a/homeassistant/components/verisure/__init__.py b/homeassistant/components/verisure/__init__.py index 393a4066002dbf..195f065ee85da8 100644 --- a/homeassistant/components/verisure/__init__.py +++ b/homeassistant/components/verisure/__init__.py @@ -11,8 +11,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['vsure==1.5.2', 'jsonpath==0.75'] - _LOGGER = logging.getLogger(__name__) ATTR_DEVICE_SERIAL = 'device_serial' diff --git a/homeassistant/components/version/sensor.py b/homeassistant/components/version/sensor.py index 7c8f2b1662a151..6aed6da17f7fd5 100644 --- a/homeassistant/components/version/sensor.py +++ b/homeassistant/components/version/sensor.py @@ -11,8 +11,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['pyhaversion==2.2.0'] - _LOGGER = logging.getLogger(__name__) ALL_IMAGES = [ diff --git a/homeassistant/components/vesync/switch.py b/homeassistant/components/vesync/switch.py index d37728624ef533..d8fa3d317ff116 100644 --- a/homeassistant/components/vesync/switch.py +++ b/homeassistant/components/vesync/switch.py @@ -6,8 +6,6 @@ import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyvesync_v2==0.9.6'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index bab54c68a908fa..7b47a388325845 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -15,8 +15,6 @@ CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['pyvizio==0.0.4'] - _LOGGER = logging.getLogger(__name__) CONF_SUPPRESS_WARNING = 'suppress_warning' diff --git a/homeassistant/components/vlc/media_player.py b/homeassistant/components/vlc/media_player.py index 41f9b5b16d4efa..be930d02b0c766 100644 --- a/homeassistant/components/vlc/media_player.py +++ b/homeassistant/components/vlc/media_player.py @@ -13,8 +13,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['python-vlc==1.1.2'] - _LOGGER = logging.getLogger(__name__) CONF_ARGUMENTS = 'arguments' diff --git a/homeassistant/components/volkszaehler/sensor.py b/homeassistant/components/volkszaehler/sensor.py index 5b808ff3c38dcb..550dc395ee72c6 100644 --- a/homeassistant/components/volkszaehler/sensor.py +++ b/homeassistant/components/volkszaehler/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['volkszaehler==0.1.2'] - _LOGGER = logging.getLogger(__name__) CONF_UUID = 'uuid' diff --git a/homeassistant/components/volvooncall/__init__.py b/homeassistant/components/volvooncall/__init__.py index 36e3959338e746..88ab41994bede3 100644 --- a/homeassistant/components/volvooncall/__init__.py +++ b/homeassistant/components/volvooncall/__init__.py @@ -21,8 +21,6 @@ DATA_KEY = DOMAIN -REQUIREMENTS = ['volvooncall==0.8.7'] - _LOGGER = logging.getLogger(__name__) MIN_UPDATE_INTERVAL = timedelta(minutes=1) diff --git a/homeassistant/components/vultr/__init__.py b/homeassistant/components/vultr/__init__.py index 9f2efabd412b06..d7f5b30507a85a 100644 --- a/homeassistant/components/vultr/__init__.py +++ b/homeassistant/components/vultr/__init__.py @@ -8,8 +8,6 @@ from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['vultr==0.1.2'] - _LOGGER = logging.getLogger(__name__) ATTR_AUTO_BACKUPS = 'auto_backups' diff --git a/homeassistant/components/vultr/binary_sensor.py b/homeassistant/components/vultr/binary_sensor.py index 87e8e93bda7b3c..087f38b77f5f9d 100644 --- a/homeassistant/components/vultr/binary_sensor.py +++ b/homeassistant/components/vultr/binary_sensor.py @@ -18,8 +18,6 @@ DEFAULT_DEVICE_CLASS = 'power' DEFAULT_NAME = 'Vultr {}' -DEPENDENCIES = ['vultr'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_SUBSCRIPTION): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string diff --git a/homeassistant/components/vultr/sensor.py b/homeassistant/components/vultr/sensor.py index f7e03dddace4e9..4f9692fe5c868f 100644 --- a/homeassistant/components/vultr/sensor.py +++ b/homeassistant/components/vultr/sensor.py @@ -15,8 +15,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Vultr {} {}' -DEPENDENCIES = ['vultr'] - MONITORED_CONDITIONS = { ATTR_CURRENT_BANDWIDTH_USED: ['Current Bandwidth Used', 'GB', 'mdi:chart-histogram'], diff --git a/homeassistant/components/vultr/switch.py b/homeassistant/components/vultr/switch.py index 502aaf9daa8767..33eeafbab68bc6 100644 --- a/homeassistant/components/vultr/switch.py +++ b/homeassistant/components/vultr/switch.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Vultr {}' -DEPENDENCIES = ['vultr'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_SUBSCRIPTION): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, diff --git a/homeassistant/components/w800rf32/__init__.py b/homeassistant/components/w800rf32/__init__.py index d2c0cf6b968fbd..920a90fbc52c5e 100644 --- a/homeassistant/components/w800rf32/__init__.py +++ b/homeassistant/components/w800rf32/__init__.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import (dispatcher_send) -REQUIREMENTS = ['pyW800rf32==0.1'] - DATA_W800RF32 = 'data_w800rf32' DOMAIN = 'w800rf32' diff --git a/homeassistant/components/w800rf32/binary_sensor.py b/homeassistant/components/w800rf32/binary_sensor.py index c942483495306d..caa3771b88e7ad 100644 --- a/homeassistant/components/w800rf32/binary_sensor.py +++ b/homeassistant/components/w800rf32/binary_sensor.py @@ -15,7 +15,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['w800rf32'] CONF_OFF_DELAY = 'off_delay' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/wake_on_lan/__init__.py b/homeassistant/components/wake_on_lan/__init__.py index e6e12ef0afe3d7..064568cdf1b513 100644 --- a/homeassistant/components/wake_on_lan/__init__.py +++ b/homeassistant/components/wake_on_lan/__init__.py @@ -7,8 +7,6 @@ from homeassistant.const import CONF_MAC import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['wakeonlan==1.1.6'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'wake_on_lan' diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index c81a476f0f8b2b..e08e531a644648 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.script import Script -REQUIREMENTS = ['wakeonlan==1.1.6'] - _LOGGER = logging.getLogger(__name__) CONF_BROADCAST_ADDRESS = 'broadcast_address' diff --git a/homeassistant/components/waqi/sensor.py b/homeassistant/components/waqi/sensor.py index f3000890de6800..451b8173562d4d 100644 --- a/homeassistant/components/waqi/sensor.py +++ b/homeassistant/components/waqi/sensor.py @@ -14,8 +14,6 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['waqiasync==1.0.0'] - _LOGGER = logging.getLogger(__name__) ATTR_DOMINENTPOL = 'dominentpol' diff --git a/homeassistant/components/waterfurnace/__init__.py b/homeassistant/components/waterfurnace/__init__.py index 38fd44cd1c7d3b..848037f584ef18 100644 --- a/homeassistant/components/waterfurnace/__init__.py +++ b/homeassistant/components/waterfurnace/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery -REQUIREMENTS = ['waterfurnace==1.1.0'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'waterfurnace' diff --git a/homeassistant/components/watson_iot/__init__.py b/homeassistant/components/watson_iot/__init__.py index e9a907ee6d2e1d..cefce56de07a8b 100644 --- a/homeassistant/components/watson_iot/__init__.py +++ b/homeassistant/components/watson_iot/__init__.py @@ -13,8 +13,6 @@ from homeassistant.helpers import state as state_helper import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['ibmiotf==0.3.4'] - _LOGGER = logging.getLogger(__name__) CONF_ORG = 'organization' diff --git a/homeassistant/components/waze_travel_time/sensor.py b/homeassistant/components/waze_travel_time/sensor.py index 984a5800898dd4..282637b15076b7 100644 --- a/homeassistant/components/waze_travel_time/sensor.py +++ b/homeassistant/components/waze_travel_time/sensor.py @@ -13,8 +13,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['WazeRouteCalculator==0.9'] - _LOGGER = logging.getLogger(__name__) ATTR_DURATION = 'duration' diff --git a/homeassistant/components/webhook/__init__.py b/homeassistant/components/webhook/__init__.py index 59be3ab1890662..7d8dda06e4d52a 100644 --- a/homeassistant/components/webhook/__init__.py +++ b/homeassistant/components/webhook/__init__.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['http'] - DOMAIN = 'webhook' URL_WEBHOOK_PATH = "/api/webhook/{webhook_id}" diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 35c3c456680d27..fa62e29f233be8 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -21,8 +21,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.script import Script -REQUIREMENTS = ['pylgtv==0.1.9', 'websockets==6.0'] - _CONFIGURING = {} # type: Dict[str, str] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/webostv/notify.py b/homeassistant/components/webostv/notify.py index 5887586df65d41..d8b1d04f8bf192 100644 --- a/homeassistant/components/webostv/notify.py +++ b/homeassistant/components/webostv/notify.py @@ -8,8 +8,6 @@ ATTR_DATA, BaseNotificationService, PLATFORM_SCHEMA) from homeassistant.const import (CONF_FILENAME, CONF_HOST, CONF_ICON) -REQUIREMENTS = ['pylgtv==0.1.9'] - _LOGGER = logging.getLogger(__name__) WEBOSTV_CONFIG_FILE = 'webostv.conf' diff --git a/homeassistant/components/wemo/__init__.py b/homeassistant/components/wemo/__init__.py index 709b3ec8672755..017538b93d4960 100644 --- a/homeassistant/components/wemo/__init__.py +++ b/homeassistant/components/wemo/__init__.py @@ -11,8 +11,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) -REQUIREMENTS = ['pywemo==0.4.34'] - DOMAIN = 'wemo' # Mapping from Wemo model_name to component. diff --git a/homeassistant/components/wemo/binary_sensor.py b/homeassistant/components/wemo/binary_sensor.py index 6606a5bd65dc77..d727190349835b 100644 --- a/homeassistant/components/wemo/binary_sensor.py +++ b/homeassistant/components/wemo/binary_sensor.py @@ -10,8 +10,6 @@ from . import SUBSCRIPTION_REGISTRY -DEPENDENCIES = ['wemo'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wemo/fan.py b/homeassistant/components/wemo/fan.py index c5f3c0a16fa44f..f635010d98d8ed 100644 --- a/homeassistant/components/wemo/fan.py +++ b/homeassistant/components/wemo/fan.py @@ -16,7 +16,6 @@ from . import SUBSCRIPTION_REGISTRY -DEPENDENCIES = ['wemo'] SCAN_INTERVAL = timedelta(seconds=10) DATA_KEY = 'fan.wemo' diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index ff7185cbf34862..2429bca892282e 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -15,8 +15,6 @@ from . import SUBSCRIPTION_REGISTRY -DEPENDENCIES = ['wemo'] - MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100) diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index 21d4cb64904aea..b8967cead3b03b 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -14,7 +14,6 @@ from . import SUBSCRIPTION_REGISTRY -DEPENDENCIES = ['wemo'] SCAN_INTERVAL = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/whois/sensor.py b/homeassistant/components/whois/sensor.py index e36bdea08c3f63..5a369190c94f6f 100644 --- a/homeassistant/components/whois/sensor.py +++ b/homeassistant/components/whois/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-whois==0.7.1'] - _LOGGER = logging.getLogger(__name__) CONF_DOMAIN = 'domain' diff --git a/homeassistant/components/wink/__init__.py b/homeassistant/components/wink/__init__.py index 2b03d7711acbd6..4e25fc4fd0dede 100644 --- a/homeassistant/components/wink/__init__.py +++ b/homeassistant/components/wink/__init__.py @@ -20,8 +20,6 @@ from homeassistant.helpers.event import track_time_interval from homeassistant.util.json import load_json, save_json -REQUIREMENTS = ['python-wink==1.10.3', 'pubnubsub-handler==1.0.3'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'wink' diff --git a/homeassistant/components/wink/alarm_control_panel.py b/homeassistant/components/wink/alarm_control_panel.py index 73ca9a3cac4ac0..61699c763ce110 100644 --- a/homeassistant/components/wink/alarm_control_panel.py +++ b/homeassistant/components/wink/alarm_control_panel.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['wink'] - STATE_ALARM_PRIVACY = 'Private' diff --git a/homeassistant/components/wink/binary_sensor.py b/homeassistant/components/wink/binary_sensor.py index f3757d7bf39624..d8f9163c46ef8b 100644 --- a/homeassistant/components/wink/binary_sensor.py +++ b/homeassistant/components/wink/binary_sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['wink'] - # These are the available sensors mapped to binary_sensor class SENSOR_TYPES = { 'brightness': 'light', diff --git a/homeassistant/components/wink/climate.py b/homeassistant/components/wink/climate.py index f5e75c1fb8d591..fd02fdd4ec3ffd 100644 --- a/homeassistant/components/wink/climate.py +++ b/homeassistant/components/wink/climate.py @@ -26,8 +26,6 @@ ATTR_HEAT_ON = 'heat_on' ATTR_COOL_ON = 'cool_on' -DEPENDENCIES = ['wink'] - SPEED_LOW = 'low' SPEED_MEDIUM = 'medium' SPEED_HIGH = 'high' diff --git a/homeassistant/components/wink/cover.py b/homeassistant/components/wink/cover.py index f4c4841c2a2d0e..b8152adbfdabd5 100644 --- a/homeassistant/components/wink/cover.py +++ b/homeassistant/components/wink/cover.py @@ -3,8 +3,6 @@ from . import DOMAIN, WinkDevice -DEPENDENCIES = ['wink'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink cover platform.""" diff --git a/homeassistant/components/wink/fan.py b/homeassistant/components/wink/fan.py index 52a27eb3c3df19..3fb06abc145726 100644 --- a/homeassistant/components/wink/fan.py +++ b/homeassistant/components/wink/fan.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['wink'] - SPEED_AUTO = 'auto' SPEED_LOWEST = 'lowest' SUPPORTED_FEATURES = SUPPORT_DIRECTION + SUPPORT_SET_SPEED diff --git a/homeassistant/components/wink/light.py b/homeassistant/components/wink/light.py index 95747bcc1b2c63..0da432f7fe3be5 100644 --- a/homeassistant/components/wink/light.py +++ b/homeassistant/components/wink/light.py @@ -8,8 +8,6 @@ from . import DOMAIN, WinkDevice -DEPENDENCIES = ['wink'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink lights.""" diff --git a/homeassistant/components/wink/lock.py b/homeassistant/components/wink/lock.py index 8e6fb9b280530e..01e038e9d09947 100644 --- a/homeassistant/components/wink/lock.py +++ b/homeassistant/components/wink/lock.py @@ -10,8 +10,6 @@ from . import DOMAIN, WinkDevice -DEPENDENCIES = ['wink'] - _LOGGER = logging.getLogger(__name__) SERVICE_SET_VACATION_MODE = 'wink_set_lock_vacation_mode' diff --git a/homeassistant/components/wink/scene.py b/homeassistant/components/wink/scene.py index e77402c4d45880..d0e03ef0688585 100644 --- a/homeassistant/components/wink/scene.py +++ b/homeassistant/components/wink/scene.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['wink'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Wink platform.""" diff --git a/homeassistant/components/wink/sensor.py b/homeassistant/components/wink/sensor.py index 3dfd704d564a36..b233089458407c 100644 --- a/homeassistant/components/wink/sensor.py +++ b/homeassistant/components/wink/sensor.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['wink'] - SENSOR_TYPES = ['temperature', 'humidity', 'balance', 'proximity'] diff --git a/homeassistant/components/wink/switch.py b/homeassistant/components/wink/switch.py index 6ee777dd1fcccd..1102087ed2a964 100644 --- a/homeassistant/components/wink/switch.py +++ b/homeassistant/components/wink/switch.py @@ -5,8 +5,6 @@ from . import DOMAIN, WinkDevice -DEPENDENCIES = ['wink'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wirelesstag/__init__.py b/homeassistant/components/wirelesstag/__init__.py index 28c8cb4d5156c2..61209a8293b5f8 100644 --- a/homeassistant/components/wirelesstag/__init__.py +++ b/homeassistant/components/wirelesstag/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.dispatcher import ( dispatcher_send) -REQUIREMENTS = ['wirelesstagpy==0.4.0'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/wirelesstag/binary_sensor.py b/homeassistant/components/wirelesstag/binary_sensor.py index aefa5ed34a9dad..7dd3e8df6cae1d 100644 --- a/homeassistant/components/wirelesstag/binary_sensor.py +++ b/homeassistant/components/wirelesstag/binary_sensor.py @@ -14,8 +14,6 @@ DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_BINARY_EVENT_UPDATE, WirelessTagBaseSensor) -DEPENDENCIES = ['wirelesstag'] - _LOGGER = logging.getLogger(__name__) # On means in range, Off means out of range diff --git a/homeassistant/components/wirelesstag/sensor.py b/homeassistant/components/wirelesstag/sensor.py index ca26e07b985ae9..bba3f1503c9b7e 100644 --- a/homeassistant/components/wirelesstag/sensor.py +++ b/homeassistant/components/wirelesstag/sensor.py @@ -12,8 +12,6 @@ from . import ( DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_TAG_UPDATE, WirelessTagBaseSensor) -DEPENDENCIES = ['wirelesstag'] - _LOGGER = logging.getLogger(__name__) SENSOR_TEMPERATURE = 'temperature' diff --git a/homeassistant/components/wirelesstag/switch.py b/homeassistant/components/wirelesstag/switch.py index 4a2b64acda1d75..c909f10c75c67d 100644 --- a/homeassistant/components/wirelesstag/switch.py +++ b/homeassistant/components/wirelesstag/switch.py @@ -9,8 +9,6 @@ from . import DOMAIN as WIRELESSTAG_DOMAIN, WirelessTagBaseSensor -DEPENDENCIES = ['wirelesstag'] - _LOGGER = logging.getLogger(__name__) ARM_TEMPERATURE = 'temperature' diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py index b505e075018acc..73fa8133c9ff38 100644 --- a/homeassistant/components/workday/binary_sensor.py +++ b/homeassistant/components/workday/binary_sensor.py @@ -9,8 +9,6 @@ from homeassistant.components.binary_sensor import BinarySensorDevice import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['holidays==0.9.10'] - _LOGGER = logging.getLogger(__name__) # List of all countries currently supported by holidays diff --git a/homeassistant/components/wunderlist/__init__.py b/homeassistant/components/wunderlist/__init__.py index d67cf089b5e752..5c85c7468266ce 100644 --- a/homeassistant/components/wunderlist/__init__.py +++ b/homeassistant/components/wunderlist/__init__.py @@ -7,8 +7,6 @@ from homeassistant.const import ( CONF_NAME, CONF_ACCESS_TOKEN) -REQUIREMENTS = ['wunderpy2==0.1.6'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'wunderlist' diff --git a/homeassistant/components/xbox_live/sensor.py b/homeassistant/components/xbox_live/sensor.py index 9f8a02686accb6..874c16296941cb 100644 --- a/homeassistant/components/xbox_live/sensor.py +++ b/homeassistant/components/xbox_live/sensor.py @@ -8,8 +8,6 @@ from homeassistant.const import (CONF_API_KEY, STATE_UNKNOWN) from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['xboxapi==0.1.1'] - _LOGGER = logging.getLogger(__name__) CONF_XUID = 'xuid' diff --git a/homeassistant/components/xeoma/camera.py b/homeassistant/components/xeoma/camera.py index dd0ee432707718..60f7ab2c972865 100644 --- a/homeassistant/components/xeoma/camera.py +++ b/homeassistant/components/xeoma/camera.py @@ -8,8 +8,6 @@ CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['pyxeoma==1.4.1'] - _LOGGER = logging.getLogger(__name__) CONF_CAMERAS = 'cameras' diff --git a/homeassistant/components/xfinity/device_tracker.py b/homeassistant/components/xfinity/device_tracker.py index 04702355de7634..bdde650091d53b 100644 --- a/homeassistant/components/xfinity/device_tracker.py +++ b/homeassistant/components/xfinity/device_tracker.py @@ -9,8 +9,6 @@ DOMAIN, PLATFORM_SCHEMA, DeviceScanner) from homeassistant.const import CONF_HOST -REQUIREMENTS = ['xfinity-gateway==0.0.4'] - _LOGGER = logging.getLogger(__name__) DEFAULT_HOST = '10.0.0.1' diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index 98e54d2bc73515..e541936ef0e700 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -11,7 +11,6 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream -DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) DEFAULT_BRAND = 'Xiaomi Home Camera' diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index 9b113170f8a751..22a8ec95c33fd8 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -16,8 +16,6 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -REQUIREMENTS = ['PyXiaomiGateway==0.12.2'] - _LOGGER = logging.getLogger(__name__) ATTR_GW_MAC = 'gw_mac' diff --git a/homeassistant/components/xiaomi_miio/device_tracker.py b/homeassistant/components/xiaomi_miio/device_tracker.py index e7ea9fbbb408d9..5e5485364dfe0b 100644 --- a/homeassistant/components/xiaomi_miio/device_tracker.py +++ b/homeassistant/components/xiaomi_miio/device_tracker.py @@ -8,8 +8,6 @@ from homeassistant.const import CONF_HOST, CONF_TOKEN import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index 51d4780160dacf..ea00cd6d95e5eb 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -13,8 +13,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Miio Device' diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index ec07a557342cca..fa853d1f83d44d 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -17,8 +17,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import color, dt -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Philips Light' diff --git a/homeassistant/components/xiaomi_miio/remote.py b/homeassistant/components/xiaomi_miio/remote.py index 450279c18253e2..7cb0cd68439a16 100644 --- a/homeassistant/components/xiaomi_miio/remote.py +++ b/homeassistant/components/xiaomi_miio/remote.py @@ -17,8 +17,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.dt import utcnow -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) SERVICE_LEARN = 'xiaomi_miio_learn_command' diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index 41d3ce65b13d84..be500f665f42f6 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -9,8 +9,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Miio Sensor' diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index d1acce02e47ca9..91924c8282148b 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -12,8 +12,6 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Miio Switch' diff --git a/homeassistant/components/xiaomi_miio/vacuum.py b/homeassistant/components/xiaomi_miio/vacuum.py index 2673a5b897ccab..ce527d41e254a1 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -16,8 +16,6 @@ ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN, STATE_OFF, STATE_ON) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['python-miio==0.4.5', 'construct==2.9.45'] - _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Vacuum cleaner' diff --git a/homeassistant/components/xiaomi_tv/media_player.py b/homeassistant/components/xiaomi_tv/media_player.py index 2c8a2e1ea83b70..862ed3bcc396ef 100644 --- a/homeassistant/components/xiaomi_tv/media_player.py +++ b/homeassistant/components/xiaomi_tv/media_player.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pymitv==1.4.3'] - DEFAULT_NAME = "Xiaomi TV" _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xmpp/notify.py b/homeassistant/components/xmpp/notify.py index d8036f5ee1e43a..79e6edafdb453f 100644 --- a/homeassistant/components/xmpp/notify.py +++ b/homeassistant/components/xmpp/notify.py @@ -17,8 +17,6 @@ from homeassistant.components.notify import ( ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['slixmpp==1.4.2'] - _LOGGER = logging.getLogger(__name__) ATTR_DATA = 'data' diff --git a/homeassistant/components/xs1/__init__.py b/homeassistant/components/xs1/__init__.py index f67eb8fd15affc..7e245dc813590e 100644 --- a/homeassistant/components/xs1/__init__.py +++ b/homeassistant/components/xs1/__init__.py @@ -11,8 +11,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['xs1-api-client==2.3.5'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'xs1' diff --git a/homeassistant/components/xs1/climate.py b/homeassistant/components/xs1/climate.py index 080b87c13469a2..1d12fcc90fa6c9 100644 --- a/homeassistant/components/xs1/climate.py +++ b/homeassistant/components/xs1/climate.py @@ -8,7 +8,6 @@ from . import ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity -DEPENDENCIES = ['xs1'] _LOGGER = logging.getLogger(__name__) MIN_TEMP = 8 diff --git a/homeassistant/components/xs1/sensor.py b/homeassistant/components/xs1/sensor.py index f5fdcf1fb34503..150c2da1f372a6 100644 --- a/homeassistant/components/xs1/sensor.py +++ b/homeassistant/components/xs1/sensor.py @@ -5,7 +5,6 @@ from . import ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity -DEPENDENCIES = ['xs1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xs1/switch.py b/homeassistant/components/xs1/switch.py index d8b344fc716a39..2513d888dd878a 100644 --- a/homeassistant/components/xs1/switch.py +++ b/homeassistant/components/xs1/switch.py @@ -7,8 +7,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['xs1'] - async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py index 1a8e03a6363120..ac1b220b12084d 100755 --- a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py +++ b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py @@ -10,8 +10,6 @@ STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['yalesmartalarmclient==0.1.6'] - CONF_AREA_ID = 'area_id' DEFAULT_NAME = 'Yale Smart Alarm' diff --git a/homeassistant/components/yamaha/media_player.py b/homeassistant/components/yamaha/media_player.py index 53c6b466f6e3a3..6ccbb1b93dbf90 100644 --- a/homeassistant/components/yamaha/media_player.py +++ b/homeassistant/components/yamaha/media_player.py @@ -18,8 +18,6 @@ STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['rxv==0.6.0'] - _LOGGER = logging.getLogger(__name__) ATTR_ENABLED = 'enabled' diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index 94002a4cc55b27..cfca4ae52f3ccb 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -15,8 +15,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pymusiccast==0.1.6'] - _LOGGER = logging.getLogger(__name__) SUPPORTED_FEATURES = ( diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 9b9778fd5d28be..8c2c9c957c6809 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -16,8 +16,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import track_time_interval -REQUIREMENTS = ['yeelight==0.4.4'] - _LOGGER = logging.getLogger(__name__) DOMAIN = "yeelight" diff --git a/homeassistant/components/yeelight/binary_sensor.py b/homeassistant/components/yeelight/binary_sensor.py index 0b44966f15c439..b2a61090a30097 100644 --- a/homeassistant/components/yeelight/binary_sensor.py +++ b/homeassistant/components/yeelight/binary_sensor.py @@ -6,8 +6,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import DATA_YEELIGHT, DATA_UPDATED -DEPENDENCIES = ['yeelight'] - _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 8aa5c3d7300c26..fa62bdc35d7c97 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -22,8 +22,6 @@ YEELIGHT_FLOW_TRANSITION_SCHEMA, ACTION_RECOVER, CONF_FLOW_PARAMS, ATTR_ACTION, ATTR_COUNT) -DEPENDENCIES = ['yeelight'] - _LOGGER = logging.getLogger(__name__) SUPPORT_YEELIGHT = (SUPPORT_BRIGHTNESS | diff --git a/homeassistant/components/yeelightsunflower/light.py b/homeassistant/components/yeelightsunflower/light.py index 9252143526bfa5..a8636a280f5cd3 100644 --- a/homeassistant/components/yeelightsunflower/light.py +++ b/homeassistant/components/yeelightsunflower/light.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_HOST import homeassistant.util.color as color_util -REQUIREMENTS = ['yeelightsunflower==0.0.10'] - _LOGGER = logging.getLogger(__name__) SUPPORT_YEELIGHT_SUNFLOWER = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR) diff --git a/homeassistant/components/yessssms/notify.py b/homeassistant/components/yessssms/notify.py index c229c361e2884f..5c3af591a1275f 100644 --- a/homeassistant/components/yessssms/notify.py +++ b/homeassistant/components/yessssms/notify.py @@ -9,8 +9,6 @@ from homeassistant.components.notify import (PLATFORM_SCHEMA, BaseNotificationService) -REQUIREMENTS = ['YesssSMS==0.2.3'] - _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/yi/camera.py b/homeassistant/components/yi/camera.py index 7ed36b97868db6..0dbb42c384ea8b 100644 --- a/homeassistant/components/yi/camera.py +++ b/homeassistant/components/yi/camera.py @@ -12,8 +12,6 @@ from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream from homeassistant.exceptions import PlatformNotReady -REQUIREMENTS = ['aioftp==0.12.0'] -DEPENDENCIES = ['ffmpeg'] _LOGGER = logging.getLogger(__name__) DEFAULT_BRAND = 'YI Home Camera' diff --git a/homeassistant/components/yr/sensor.py b/homeassistant/components/yr/sensor.py index 4c898a7c9fe64d..c9f57abf5d916c 100644 --- a/homeassistant/components/yr/sensor.py +++ b/homeassistant/components/yr/sensor.py @@ -20,8 +20,6 @@ async_call_later) from homeassistant.util import dt as dt_util -REQUIREMENTS = ['xmltodict==0.11.0'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Weather forecast from met.no, delivered by the Norwegian " \ diff --git a/homeassistant/components/yweather/sensor.py b/homeassistant/components/yweather/sensor.py index 129532ceb57477..fc49d79d11051a 100644 --- a/homeassistant/components/yweather/sensor.py +++ b/homeassistant/components/yweather/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['yahooweather==0.10'] - _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Weather details provided by Yahoo! Inc." diff --git a/homeassistant/components/yweather/weather.py b/homeassistant/components/yweather/weather.py index e4eb34a039ac62..4d7986d8a5cb01 100644 --- a/homeassistant/components/yweather/weather.py +++ b/homeassistant/components/yweather/weather.py @@ -10,8 +10,6 @@ from homeassistant.const import CONF_NAME, STATE_UNKNOWN, TEMP_CELSIUS import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ["yahooweather==0.10"] - _LOGGER = logging.getLogger(__name__) DATA_CONDITION = 'yahoo_condition' diff --git a/homeassistant/components/zabbix/__init__.py b/homeassistant/components/zabbix/__init__.py index f33c60b1c3930b..041f67b37ee15e 100644 --- a/homeassistant/components/zabbix/__init__.py +++ b/homeassistant/components/zabbix/__init__.py @@ -8,8 +8,6 @@ CONF_PATH, CONF_HOST, CONF_SSL, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['pyzabbix==0.7.4'] - _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = False diff --git a/homeassistant/components/zabbix/sensor.py b/homeassistant/components/zabbix/sensor.py index ae2e70ede2c426..004c176570ab10 100644 --- a/homeassistant/components/zabbix/sensor.py +++ b/homeassistant/components/zabbix/sensor.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zabbix'] - _CONF_TRIGGERS = 'triggers' _CONF_HOSTIDS = 'hostids' _CONF_INDIVIDUAL = 'individual' diff --git a/homeassistant/components/zengge/light.py b/homeassistant/components/zengge/light.py index 8bbd56a483ec12..e066ad9da65956 100644 --- a/homeassistant/components/zengge/light.py +++ b/homeassistant/components/zengge/light.py @@ -10,8 +10,6 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util -REQUIREMENTS = ['zengge==0.2'] - _LOGGER = logging.getLogger(__name__) SUPPORT_ZENGGE_LED = (SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_WHITE_VALUE) diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 844246528a60db..d2dcd907885782 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -7,11 +7,8 @@ from homeassistant import util from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, __version__) -REQUIREMENTS = ['zeroconf==0.21.3'] - _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['api'] DOMAIN = 'zeroconf' diff --git a/homeassistant/components/zestimate/sensor.py b/homeassistant/components/zestimate/sensor.py index f69e3b16ebe584..e66aad701b7972 100644 --- a/homeassistant/components/zestimate/sensor.py +++ b/homeassistant/components/zestimate/sensor.py @@ -12,8 +12,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -REQUIREMENTS = ['xmltodict==0.11.0'] - _LOGGER = logging.getLogger(__name__) _RESOURCE = 'http://www.zillow.com/webservice/GetZestimate.htm' diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 08362eba082845..647601701507a0 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -20,14 +20,6 @@ from .core.patches import apply_cluster_listener_patch from .core.registries import establish_device_mappings -REQUIREMENTS = [ - 'bellows-homeassistant==0.7.2', - 'zigpy-homeassistant==0.3.1', - 'zigpy-xbee-homeassistant==0.1.3', - 'zha-quirks==0.0.7', - 'zigpy-deconz==0.1.3' -] - DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({ vol.Optional(ha_const.CONF_TYPE): cv.string, }) diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index b4254eb83e7a49..e9fa25c2577897 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -14,8 +14,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zha'] - # Zigbee Cluster Library Zone Type to Home Assistant device class CLASS_MAPPING = { 0x000d: 'motion', diff --git a/homeassistant/components/zha/fan.py b/homeassistant/components/zha/fan.py index b80834af1d7d20..9619049bc7f72a 100644 --- a/homeassistant/components/zha/fan.py +++ b/homeassistant/components/zha/fan.py @@ -12,8 +12,6 @@ ) from .entity import ZhaEntity -DEPENDENCIES = ['zha'] - _LOGGER = logging.getLogger(__name__) # Additional speeds in zigbee's ZCL diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index 12bc12c5f6edb1..ec840d5edb39de 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -17,8 +17,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zha'] - DEFAULT_DURATION = 5 CAPABILITIES_COLOR_XY = 0x08 diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 13932d7dd7ab1b..b6ac70fa1876b6 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -16,8 +16,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zha'] - # Formatter functions def pass_through_formatter(value): diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 34c9ab2514d003..7efcbabd74e1be 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -13,8 +13,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zha'] - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): diff --git a/homeassistant/components/zhong_hong/climate.py b/homeassistant/components/zhong_hong/climate.py index 7fd2b971009e17..d01d1028507e81 100644 --- a/homeassistant/components/zhong_hong/climate.py +++ b/homeassistant/components/zhong_hong/climate.py @@ -14,8 +14,6 @@ from homeassistant.helpers.dispatcher import (async_dispatcher_connect, async_dispatcher_send) -REQUIREMENTS = ['zhong_hong_hvac==1.0.9'] - _LOGGER = logging.getLogger(__name__) CONF_GATEWAY_ADDRRESS = 'gateway_address' diff --git a/homeassistant/components/zigbee/__init__.py b/homeassistant/components/zigbee/__init__.py index 0e2d3587829c92..516ac3453c846d 100644 --- a/homeassistant/components/zigbee/__init__.py +++ b/homeassistant/components/zigbee/__init__.py @@ -11,8 +11,6 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, dispatcher_send) -REQUIREMENTS = ['xbee-helper==0.0.7'] - _LOGGER = logging.getLogger(__name__) DOMAIN = 'zigbee' diff --git a/homeassistant/components/zigbee/binary_sensor.py b/homeassistant/components/zigbee/binary_sensor.py index ccf4e70df34a18..8cf7f4d7dc02e9 100644 --- a/homeassistant/components/zigbee/binary_sensor.py +++ b/homeassistant/components/zigbee/binary_sensor.py @@ -8,8 +8,6 @@ CONF_ON_STATE = 'on_state' DEFAULT_ON_STATE = 'high' -DEPENDENCIES = ['zigbee'] - STATES = ['high', 'low'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/zigbee/light.py b/homeassistant/components/zigbee/light.py index b9be0d893239d0..1ff38af02e48b4 100644 --- a/homeassistant/components/zigbee/light.py +++ b/homeassistant/components/zigbee/light.py @@ -8,8 +8,6 @@ CONF_ON_STATE = 'on_state' DEFAULT_ON_STATE = 'high' -DEPENDENCIES = ['zigbee'] - STATES = ['high', 'low'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/zigbee/sensor.py b/homeassistant/components/zigbee/sensor.py index 48301ac9728ee5..480064c5715d3c 100644 --- a/homeassistant/components/zigbee/sensor.py +++ b/homeassistant/components/zigbee/sensor.py @@ -16,8 +16,6 @@ CONF_MAX_VOLTS = 'max_volts' DEFAULT_VOLTS = 1.2 -DEPENDENCIES = ['zigbee'] - TYPES = ['analog', 'temperature'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ diff --git a/homeassistant/components/zigbee/switch.py b/homeassistant/components/zigbee/switch.py index ddfd47a047e9c6..26a9e8fac835c3 100644 --- a/homeassistant/components/zigbee/switch.py +++ b/homeassistant/components/zigbee/switch.py @@ -5,12 +5,10 @@ from . import PLATFORM_SCHEMA, ZigBeeDigitalOut, ZigBeeDigitalOutConfig -DEPENDENCIES = ['zigbee'] CONF_ON_STATE = 'on_state' DEFAULT_ON_STATE = 'high' -DEPENDENCIES = ['zigbee'] STATES = ['high', 'low'] diff --git a/homeassistant/components/ziggo_mediabox_xl/media_player.py b/homeassistant/components/ziggo_mediabox_xl/media_player.py index 574d08e97a4504..9bbc61869240aa 100644 --- a/homeassistant/components/ziggo_mediabox_xl/media_player.py +++ b/homeassistant/components/ziggo_mediabox_xl/media_player.py @@ -14,8 +14,6 @@ CONF_HOST, CONF_NAME, STATE_OFF, STATE_PAUSED, STATE_PLAYING) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['ziggo-mediabox-xl==1.1.0'] - _LOGGER = logging.getLogger(__name__) DATA_KNOWN_DEVICES = 'ziggo_mediabox_xl_known_devices' diff --git a/homeassistant/components/zoneminder/__init__.py b/homeassistant/components/zoneminder/__init__.py index a4d90d523aacca..4e2585a34e3f83 100644 --- a/homeassistant/components/zoneminder/__init__.py +++ b/homeassistant/components/zoneminder/__init__.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['zm-py==0.3.3'] - CONF_PATH_ZMS = 'path_zms' DEFAULT_PATH = '/zm/' diff --git a/homeassistant/components/zoneminder/binary_sensor.py b/homeassistant/components/zoneminder/binary_sensor.py index ce59d4573bea1d..23196cf571fe95 100644 --- a/homeassistant/components/zoneminder/binary_sensor.py +++ b/homeassistant/components/zoneminder/binary_sensor.py @@ -3,8 +3,6 @@ from . import DOMAIN as ZONEMINDER_DOMAIN -DEPENDENCIES = ['zoneminder'] - async def async_setup_platform( hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/zoneminder/camera.py b/homeassistant/components/zoneminder/camera.py index fe3333fa3ed274..da625e6ee462df 100644 --- a/homeassistant/components/zoneminder/camera.py +++ b/homeassistant/components/zoneminder/camera.py @@ -9,8 +9,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zoneminder'] - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the ZoneMinder cameras.""" diff --git a/homeassistant/components/zoneminder/sensor.py b/homeassistant/components/zoneminder/sensor.py index e205d921422be2..6a44d335a3e95e 100644 --- a/homeassistant/components/zoneminder/sensor.py +++ b/homeassistant/components/zoneminder/sensor.py @@ -12,8 +12,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zoneminder'] - CONF_INCLUDE_ARCHIVED = "include_archived" DEFAULT_INCLUDE_ARCHIVED = False diff --git a/homeassistant/components/zoneminder/switch.py b/homeassistant/components/zoneminder/switch.py index 78e72c5fd4a88f..d70ba8f8fd8858 100644 --- a/homeassistant/components/zoneminder/switch.py +++ b/homeassistant/components/zoneminder/switch.py @@ -11,8 +11,6 @@ _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['zoneminder'] - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_COMMAND_ON): cv.string, vol.Required(CONF_COMMAND_OFF): cv.string, diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 2d575d99647d3b..3240e389bb4a1a 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -37,8 +37,6 @@ from .util import (check_node_schema, check_value_schema, node_name, check_has_unique_id, is_node_parsed) -REQUIREMENTS = ['pydispatcher==2.0.5', 'homeassistant-pyozw==0.1.4'] - _LOGGER = logging.getLogger(__name__) CLASS_ID = 'class_id' From f269135ae925de9a0cf8d90750da9a6ff323415e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 12 Apr 2019 10:14:16 -0700 Subject: [PATCH 587/605] Migrate check-config to use get_integration (#23026) * Migrate check-config to use get_integration * ImportError --- homeassistant/scripts/check_config.py | 25 +++++++++++++++++++++---- tests/scripts/test_check_config.py | 5 +++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 1b8c67193950fc..2eb895603dd4d7 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -329,8 +329,16 @@ def _comp_error(ex, domain, config): # Process and validate config for domain in components: - component = loader.get_component(hass, domain) - if not component: + try: + integration = hass.loop.run_until_complete( + loader.async_get_integration(hass, domain)) + except loader.IntegrationNotFound: + result.add_error("Integration not found: {}".format(domain)) + continue + + try: + component = integration.get_component() + except ImportError: result.add_error("Component not found: {}".format(domain)) continue @@ -368,9 +376,18 @@ def _comp_error(ex, domain, config): platforms.append(p_validated) continue - platform = loader.get_platform(hass, domain, p_name) + try: + p_integration = hass.loop.run_until_complete( + loader.async_get_integration(hass, p_name)) + except loader.IntegrationNotFound: + result.add_error( + "Integration {} not found when trying to verify its {} " + "platform.".format(p_name, domain)) + continue - if platform is None: + try: + platform = p_integration.get_platform(domain) + except ImportError: result.add_error( "Platform not found: {}.{}".format(domain, p_name)) continue diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index 217f26e71f71b3..dd0289d44be832 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -90,7 +90,7 @@ def test_component_platform_not_found(self, isfile_patch): res = check_config.check(get_test_config_dir()) assert res['components'].keys() == {'homeassistant'} assert res['except'] == { - check_config.ERROR_STR: ['Component not found: beer']} + check_config.ERROR_STR: ['Integration not found: beer']} assert res['secret_cache'] == {} assert res['secrets'] == {} assert len(res['yaml_files']) == 1 @@ -104,7 +104,8 @@ def test_component_platform_not_found(self, isfile_patch): assert res['components']['light'] == [] assert res['except'] == { check_config.ERROR_STR: [ - 'Platform not found: light.beer', + 'Integration beer not found when trying to verify its ' + 'light platform.', ]} assert res['secret_cache'] == {} assert res['secrets'] == {} From 0a3e11aa12e49977f95c211989e6600d7f5df58b Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 12 Apr 2019 20:11:36 +0200 Subject: [PATCH 588/605] Move Broadlink services to component (#21465) * Register services in broadlink domain * Add tests for broadlink services * Resolve review comments * One more review fix * Restore auth retry * Drop unused constants * Fix flake8 errors --- .../components/broadlink/__init__.py | 110 ++++++++++++++++ homeassistant/components/broadlink/const.py | 7 ++ .../components/broadlink/services.yaml | 9 ++ homeassistant/components/broadlink/switch.py | 71 +---------- tests/components/broadlink/__init__.py | 1 + tests/components/broadlink/test_init.py | 117 ++++++++++++++++++ 6 files changed, 250 insertions(+), 65 deletions(-) create mode 100644 homeassistant/components/broadlink/const.py create mode 100644 homeassistant/components/broadlink/services.yaml create mode 100644 tests/components/broadlink/__init__.py create mode 100644 tests/components/broadlink/test_init.py diff --git a/homeassistant/components/broadlink/__init__.py b/homeassistant/components/broadlink/__init__.py index 5055c7fa597f61..3404bdef99b2c6 100644 --- a/homeassistant/components/broadlink/__init__.py +++ b/homeassistant/components/broadlink/__init__.py @@ -1 +1,111 @@ """The broadlink component.""" +import asyncio +from base64 import b64decode, b64encode +import logging +import re +import socket + +from datetime import timedelta +import voluptuous as vol + +from homeassistant.const import CONF_HOST +import homeassistant.helpers.config_validation as cv +from homeassistant.util.dt import utcnow + +from .const import CONF_PACKET, DOMAIN, SERVICE_LEARN, SERVICE_SEND + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_RETRY = 3 + + +def ipv4_address(value): + """Validate an ipv4 address.""" + regex = re.compile(r'^\d+\.\d+\.\d+\.\d+$') + if not regex.match(value): + raise vol.Invalid('Invalid Ipv4 address, expected a.b.c.d') + return value + + +def data_packet(value): + """Decode a data packet given for broadlink.""" + return b64decode(cv.string(value)) + + +SERVICE_SEND_SCHEMA = vol.Schema({ + vol.Required(CONF_HOST): ipv4_address, + vol.Required(CONF_PACKET): vol.All(cv.ensure_list, [data_packet]) +}) + +SERVICE_LEARN_SCHEMA = vol.Schema({ + vol.Required(CONF_HOST): ipv4_address, +}) + + +def async_setup_service(hass, host, device): + """Register a device for given host for use in services.""" + hass.data.setdefault(DOMAIN, {})[host] = device + + if not hass.services.has_service(DOMAIN, SERVICE_LEARN): + + async def _learn_command(call): + """Learn a packet from remote.""" + device = hass.data[DOMAIN][call.data[CONF_HOST]] + + try: + auth = await hass.async_add_executor_job(device.auth) + except socket.timeout: + _LOGGER.error("Failed to connect to device, timeout") + return + if not auth: + _LOGGER.error("Failed to connect to device") + return + + await hass.async_add_executor_job(device.enter_learning) + + _LOGGER.info("Press the key you want Home Assistant to learn") + start_time = utcnow() + while (utcnow() - start_time) < timedelta(seconds=20): + packet = await hass.async_add_executor_job( + device.check_data) + if packet: + data = b64encode(packet).decode('utf8') + log_msg = "Received packet is: {}".\ + format(data) + _LOGGER.info(log_msg) + hass.components.persistent_notification.async_create( + log_msg, title='Broadlink switch') + return + await asyncio.sleep(1) + _LOGGER.error("No signal was received") + hass.components.persistent_notification.async_create( + "No signal was received", title='Broadlink switch') + + hass.services.async_register( + DOMAIN, SERVICE_LEARN, _learn_command, + schema=SERVICE_LEARN_SCHEMA) + + if not hass.services.has_service(DOMAIN, SERVICE_SEND): + + async def _send_packet(call): + """Send a packet.""" + device = hass.data[DOMAIN][call.data[CONF_HOST]] + packets = call.data[CONF_PACKET] + for packet in packets: + for retry in range(DEFAULT_RETRY): + try: + await hass.async_add_executor_job( + device.send_data, packet) + break + except (socket.timeout, ValueError): + try: + await hass.async_add_executor_job( + device.auth) + except socket.timeout: + if retry == DEFAULT_RETRY-1: + _LOGGER.error( + "Failed to send packet to device") + + hass.services.async_register( + DOMAIN, SERVICE_SEND, _send_packet, + schema=SERVICE_SEND_SCHEMA) diff --git a/homeassistant/components/broadlink/const.py b/homeassistant/components/broadlink/const.py new file mode 100644 index 00000000000000..1c4e0ae7948880 --- /dev/null +++ b/homeassistant/components/broadlink/const.py @@ -0,0 +1,7 @@ +"""Constants for broadlink platform.""" +CONF_PACKET = 'packet' + +DOMAIN = 'broadlink' + +SERVICE_LEARN = 'learn' +SERVICE_SEND = 'send' diff --git a/homeassistant/components/broadlink/services.yaml b/homeassistant/components/broadlink/services.yaml new file mode 100644 index 00000000000000..2281cb1cc4d05e --- /dev/null +++ b/homeassistant/components/broadlink/services.yaml @@ -0,0 +1,9 @@ +send: + description: Send a raw packet to device. + fields: + host: {description: IP address of device to send packet via. This must be an already configured device., example: "192.168.0.1"} + packet: {description: base64 encoded packet.} +learn: + description: Learn a IR or RF code from remote. + fields: + host: {description: IP address of device to send packet via. This must be an already configured device., example: "192.168.0.1"} diff --git a/homeassistant/components/broadlink/switch.py b/homeassistant/components/broadlink/switch.py index f2f7b4a5d957f8..e79d78774e99e0 100644 --- a/homeassistant/components/broadlink/switch.py +++ b/homeassistant/components/broadlink/switch.py @@ -1,6 +1,5 @@ """Support for Broadlink RM devices.""" -import asyncio -from base64 import b64decode, b64encode +from base64 import b64decode import binascii from datetime import timedelta import logging @@ -9,13 +8,14 @@ import voluptuous as vol from homeassistant.components.switch import ( - DOMAIN, PLATFORM_SCHEMA, SwitchDevice, ENTITY_ID_FORMAT) + ENTITY_ID_FORMAT, PLATFORM_SCHEMA, SwitchDevice) from homeassistant.const import ( CONF_COMMAND_OFF, CONF_COMMAND_ON, CONF_FRIENDLY_NAME, CONF_HOST, CONF_MAC, CONF_SWITCHES, CONF_TIMEOUT, CONF_TYPE) import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle, slugify -from homeassistant.util.dt import utcnow + +from . import async_setup_service _LOGGER = logging.getLogger(__name__) @@ -23,9 +23,6 @@ DEFAULT_NAME = 'Broadlink switch' DEFAULT_TIMEOUT = 10 -DEFAULT_RETRY = 3 -SERVICE_LEARN = 'broadlink_learn_command' -SERVICE_SEND = 'broadlink_send_packet' CONF_SLOTS = 'slots' RM_TYPES = ['rm', 'rm2', 'rm_mini', 'rm_pro_phicomm', 'rm2_home_plus', @@ -73,57 +70,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None): config.get(CONF_MAC).encode().replace(b':', b'')) switch_type = config.get(CONF_TYPE) - async def _learn_command(call): - """Handle a learn command.""" - try: - auth = await hass.async_add_job(broadlink_device.auth) - except socket.timeout: - _LOGGER.error("Failed to connect to device, timeout") - return - if not auth: - _LOGGER.error("Failed to connect to device") - return - - await hass.async_add_job(broadlink_device.enter_learning) - - _LOGGER.info("Press the key you want Home Assistant to learn") - start_time = utcnow() - while (utcnow() - start_time) < timedelta(seconds=20): - packet = await hass.async_add_job( - broadlink_device.check_data) - if packet: - log_msg = "Received packet is: {}".\ - format(b64encode(packet).decode('utf8')) - _LOGGER.info(log_msg) - hass.components.persistent_notification.async_create( - log_msg, title='Broadlink switch') - return - await asyncio.sleep(1, loop=hass.loop) - _LOGGER.error("Did not received any signal") - hass.components.persistent_notification.async_create( - "Did not received any signal", title='Broadlink switch') - - async def _send_packet(call): - """Send a packet.""" - packets = call.data.get('packet', []) - for packet in packets: - for retry in range(DEFAULT_RETRY): - try: - extra = len(packet) % 4 - if extra > 0: - packet = packet + ('=' * (4 - extra)) - payload = b64decode(packet) - await hass.async_add_job( - broadlink_device.send_data, payload) - break - except (socket.timeout, ValueError): - try: - await hass.async_add_job( - broadlink_device.auth) - except socket.timeout: - if retry == DEFAULT_RETRY-1: - _LOGGER.error("Failed to send packet to device") - def _get_mp1_slot_name(switch_friendly_name, slot): """Get slot name.""" if not slots['slot_{}'.format(slot)]: @@ -132,13 +78,8 @@ def _get_mp1_slot_name(switch_friendly_name, slot): if switch_type in RM_TYPES: broadlink_device = broadlink.rm((ip_addr, 80), mac_addr, None) - hass.services.register(DOMAIN, SERVICE_LEARN + '_' + - slugify(ip_addr.replace('.', '_')), - _learn_command) - hass.services.register(DOMAIN, SERVICE_SEND + '_' + - slugify(ip_addr.replace('.', '_')), - _send_packet, - vol.Schema({'packet': cv.ensure_list})) + hass.add_job(async_setup_service, hass, ip_addr, broadlink_device) + switches = [] for object_id, device_config in devices.items(): switches.append( diff --git a/tests/components/broadlink/__init__.py b/tests/components/broadlink/__init__.py new file mode 100644 index 00000000000000..c2d16b9ab2abae --- /dev/null +++ b/tests/components/broadlink/__init__.py @@ -0,0 +1 @@ +"""The tests for broadlink platforms.""" diff --git a/tests/components/broadlink/test_init.py b/tests/components/broadlink/test_init.py new file mode 100644 index 00000000000000..5dca559cb0e52e --- /dev/null +++ b/tests/components/broadlink/test_init.py @@ -0,0 +1,117 @@ +"""The tests for the broadlink component.""" +from datetime import timedelta +from base64 import b64decode +from unittest.mock import MagicMock, patch, call + +import pytest +import voluptuous as vol + +from homeassistant.util.dt import utcnow +from homeassistant.components.broadlink import async_setup_service +from homeassistant.components.broadlink.const import ( + DOMAIN, SERVICE_LEARN, SERVICE_SEND) + +DUMMY_IR_PACKET = ("JgBGAJKVETkRORA6ERQRFBEUERQRFBE5ETkQOhAVEBUQFREUEBUQ" + "OhEUERQRORE5EBURFBA6EBUQOhE5EBUQFRA6EDoRFBEADQUAAA==") +DUMMY_HOST = "192.168.0.2" + + +@pytest.fixture(autouse=True) +def dummy_broadlink(): + """Mock broadlink module so we don't have that dependency on tests.""" + broadlink = MagicMock() + with patch.dict('sys.modules', { + 'broadlink': broadlink, + }): + yield broadlink + + +async def test_send(hass): + """Test send service.""" + mock_device = MagicMock() + mock_device.send_data.return_value = None + + async_setup_service(hass, DUMMY_HOST, mock_device) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_SEND, { + "host": DUMMY_HOST, + "packet": (DUMMY_IR_PACKET) + }) + await hass.async_block_till_done() + + assert mock_device.send_data.call_count == 1 + assert mock_device.send_data.call_args == call( + b64decode(DUMMY_IR_PACKET)) + + +async def test_learn(hass): + """Test learn service.""" + mock_device = MagicMock() + mock_device.enter_learning.return_value = None + mock_device.check_data.return_value = b64decode(DUMMY_IR_PACKET) + + with patch.object(hass.components.persistent_notification, + 'async_create') as mock_create: + + async_setup_service(hass, DUMMY_HOST, mock_device) + await hass.async_block_till_done() + + await hass.services.async_call(DOMAIN, SERVICE_LEARN, { + "host": DUMMY_HOST, + }) + await hass.async_block_till_done() + + assert mock_device.enter_learning.call_count == 1 + assert mock_device.enter_learning.call_args == call() + + assert mock_create.call_count == 1 + assert mock_create.call_args == call( + "Received packet is: {}".format(DUMMY_IR_PACKET), + title='Broadlink switch') + + +async def test_learn_timeout(hass): + """Test learn service.""" + mock_device = MagicMock() + mock_device.enter_learning.return_value = None + mock_device.check_data.return_value = None + + async_setup_service(hass, DUMMY_HOST, mock_device) + await hass.async_block_till_done() + + now = utcnow() + + with patch.object(hass.components.persistent_notification, + 'async_create') as mock_create, \ + patch('homeassistant.components.broadlink.utcnow') as mock_utcnow: + + mock_utcnow.side_effect = [now, now + timedelta(20)] + + await hass.services.async_call(DOMAIN, SERVICE_LEARN, { + "host": DUMMY_HOST, + }) + await hass.async_block_till_done() + + assert mock_device.enter_learning.call_count == 1 + assert mock_device.enter_learning.call_args == call() + + assert mock_create.call_count == 1 + assert mock_create.call_args == call( + "No signal was received", + title='Broadlink switch') + + +async def test_ipv4(): + """Test ipv4 parsing.""" + from homeassistant.components.broadlink import ipv4_address + + schema = vol.Schema(ipv4_address) + + for value in ('invalid', '1', '192', '192.168', + '192.168.0', '192.168.0.A'): + with pytest.raises(vol.MultipleInvalid): + schema(value) + + for value in ('192.168.0.1', '10.0.0.1'): + schema(value) From 51508d69ad91c0bca1a7c326841ccf95b8f03fa7 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 12 Apr 2019 22:28:59 +0100 Subject: [PATCH 589/605] Bandage telegram bot (#23022) * Bandage * lint * move everything into __init__.py * fix lint --- .../components/telegram_bot/__init__.py | 87 +++++++++++-------- .../components/telegram_bot/broadcast.py | 4 +- .../components/telegram_bot/polling.py | 6 +- .../components/telegram_bot/webhooks.py | 29 +------ 4 files changed, 58 insertions(+), 68 deletions(-) diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index a77b86038536e6..66f074273efbb1 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -1,5 +1,6 @@ """Support to send and receive Telegram messages.""" import io +from ipaddress import ip_network from functools import partial import importlib import logging @@ -12,7 +13,7 @@ ATTR_DATA, ATTR_MESSAGE, ATTR_TITLE) from homeassistant.const import ( ATTR_COMMAND, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_API_KEY, - CONF_PLATFORM, CONF_TIMEOUT, HTTP_DIGEST_AUTHENTICATION) + CONF_PLATFORM, CONF_TIMEOUT, HTTP_DIGEST_AUTHENTICATION, CONF_URL) import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import TemplateError @@ -51,6 +52,7 @@ CONF_ALLOWED_CHAT_IDS = 'allowed_chat_ids' CONF_PROXY_URL = 'proxy_url' CONF_PROXY_PARAMS = 'proxy_params' +CONF_TRUSTED_NETWORKS = 'trusted_networks' DOMAIN = 'telegram_bot' @@ -73,17 +75,34 @@ PARSER_HTML = 'html' PARSER_MD = 'markdown' -PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ - vol.Required(CONF_PLATFORM): vol.In(('broadcast', 'polling', 'webhooks')), - vol.Required(CONF_API_KEY): cv.string, - vol.Required(CONF_ALLOWED_CHAT_IDS): - vol.All(cv.ensure_list, [vol.Coerce(int)]), - vol.Optional(ATTR_PARSER, default=PARSER_MD): cv.string, - vol.Optional(CONF_PROXY_URL): cv.string, - vol.Optional(CONF_PROXY_PARAMS): dict, -}) - -PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE.extend(PLATFORM_SCHEMA.schema) +DEFAULT_TRUSTED_NETWORKS = [ + ip_network('149.154.167.197/32'), + ip_network('149.154.167.198/31'), + ip_network('149.154.167.200/29'), + ip_network('149.154.167.208/28'), + ip_network('149.154.167.224/29'), + ip_network('149.154.167.232/31') +] + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.All(cv.ensure_list, [ + vol.Schema({ + vol.Required(CONF_PLATFORM): vol.In( + ('broadcast', 'polling', 'webhooks')), + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_ALLOWED_CHAT_IDS): + vol.All(cv.ensure_list, [vol.Coerce(int)]), + vol.Optional(ATTR_PARSER, default=PARSER_MD): cv.string, + vol.Optional(CONF_PROXY_URL): cv.string, + vol.Optional(CONF_PROXY_PARAMS): dict, + # webhooks + vol.Optional(CONF_URL): cv.url, + vol.Optional(CONF_TRUSTED_NETWORKS, + default=DEFAULT_TRUSTED_NETWORKS): + vol.All(cv.ensure_list, [ip_network]) + }) + ]) +}, extra=vol.ALLOW_EXTRA) BASE_SERVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_TARGET): vol.All(cv.ensure_list, [vol.Coerce(int)]), @@ -213,33 +232,33 @@ async def async_setup(hass, config): if not config[DOMAIN]: return False - p_config = config[DOMAIN][0] + for p_config in config[DOMAIN]: - p_type = p_config.get(CONF_PLATFORM) + p_type = p_config.get(CONF_PLATFORM) - platform = importlib.import_module('.{}'.format(config[CONF_PLATFORM]), - __name__) + platform = importlib.import_module( + '.{}'.format(p_config[CONF_PLATFORM]), __name__) - _LOGGER.info("Setting up %s.%s", DOMAIN, p_type) - try: - receiver_service = await \ - platform.async_setup_platform(hass, p_config) - if receiver_service is False: - _LOGGER.error( - "Failed to initialize Telegram bot %s", p_type) + _LOGGER.info("Setting up %s.%s", DOMAIN, p_type) + try: + receiver_service = await \ + platform.async_setup_platform(hass, p_config) + if receiver_service is False: + _LOGGER.error( + "Failed to initialize Telegram bot %s", p_type) + return False + + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Error setting up platform %s", p_type) return False - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Error setting up platform %s", p_type) - return False - - bot = initialize_bot(p_config) - notify_service = TelegramNotificationService( - hass, - bot, - p_config.get(CONF_ALLOWED_CHAT_IDS), - p_config.get(ATTR_PARSER) - ) + bot = initialize_bot(p_config) + notify_service = TelegramNotificationService( + hass, + bot, + p_config.get(CONF_ALLOWED_CHAT_IDS), + p_config.get(ATTR_PARSER) + ) async def async_send_telegram_message(service): """Handle sending Telegram Bot message service calls.""" diff --git a/homeassistant/components/telegram_bot/broadcast.py b/homeassistant/components/telegram_bot/broadcast.py index a129ebf6604b7e..e78c28bd0c4f40 100644 --- a/homeassistant/components/telegram_bot/broadcast.py +++ b/homeassistant/components/telegram_bot/broadcast.py @@ -1,12 +1,10 @@ """Support for Telegram bot to send messages only.""" import logging -from . import PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA, initialize_bot +from . import initialize_bot _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = TELEGRAM_PLATFORM_SCHEMA - async def async_setup_platform(hass, config): """Set up the Telegram broadcast platform.""" diff --git a/homeassistant/components/telegram_bot/polling.py b/homeassistant/components/telegram_bot/polling.py index 7d0039319e3a66..0d3a8810911244 100644 --- a/homeassistant/components/telegram_bot/polling.py +++ b/homeassistant/components/telegram_bot/polling.py @@ -5,14 +5,10 @@ EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) from homeassistant.core import callback -from . import ( - CONF_ALLOWED_CHAT_IDS, PLATFORM_SCHEMA as TELEGRAM_PLATFORM_SCHEMA, - BaseTelegramBotEntity, initialize_bot) +from . import (CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity, initialize_bot) _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = TELEGRAM_PLATFORM_SCHEMA - async def async_setup_platform(hass, config): """Set up the Telegram polling platform.""" diff --git a/homeassistant/components/telegram_bot/webhooks.py b/homeassistant/components/telegram_bot/webhooks.py index 1a2839b176e04e..b3b6add0a1c65a 100644 --- a/homeassistant/components/telegram_bot/webhooks.py +++ b/homeassistant/components/telegram_bot/webhooks.py @@ -1,43 +1,20 @@ """Support for Telegram bots using webhooks.""" import datetime as dt -from ipaddress import ip_network import logging -import voluptuous as vol - from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.const import KEY_REAL_IP from homeassistant.const import ( - CONF_URL, EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED) -import homeassistant.helpers.config_validation as cv + EVENT_HOMEASSISTANT_STOP, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED) -from . import ( - CONF_ALLOWED_CHAT_IDS, PLATFORM_SCHEMA, BaseTelegramBotEntity, - initialize_bot) +from . import (CONF_ALLOWED_CHAT_IDS, CONF_TRUSTED_NETWORKS, CONF_URL, + BaseTelegramBotEntity, initialize_bot) _LOGGER = logging.getLogger(__name__) TELEGRAM_HANDLER_URL = '/api/telegram_webhooks' REMOVE_HANDLER_URL = '' -CONF_TRUSTED_NETWORKS = 'trusted_networks' - -DEFAULT_TRUSTED_NETWORKS = [ - ip_network('149.154.167.197/32'), - ip_network('149.154.167.198/31'), - ip_network('149.154.167.200/29'), - ip_network('149.154.167.208/28'), - ip_network('149.154.167.224/29'), - ip_network('149.154.167.232/31') -] - -# pylint: disable=no-value-for-parameter -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_URL): vol.Url(), - vol.Optional(CONF_TRUSTED_NETWORKS, default=DEFAULT_TRUSTED_NETWORKS): - vol.All(cv.ensure_list, [ip_network]) -}) - async def async_setup_platform(hass, config): """Set up the Telegram webhooks platform.""" From 05f267de6e9b30d79cc64219c0f61ecdfdc68abe Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 12 Apr 2019 17:44:04 -0600 Subject: [PATCH 590/605] Update RainMachine sensors in parallel (#23057) --- .../components/rainmachine/__init__.py | 27 ++++++++++++++---- .../components/rainmachine/binary_sensor.py | 28 +++++++++++-------- homeassistant/components/rainmachine/const.py | 3 ++ .../components/rainmachine/sensor.py | 5 ++-- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index f5875558a53ba1..ed46f50ec3bf4a 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -1,4 +1,5 @@ """Support for RainMachine devices.""" +import asyncio import logging from datetime import timedelta from functools import wraps @@ -20,7 +21,8 @@ from .config_flow import configured_instances from .const import ( - DATA_CLIENT, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_SSL, DOMAIN) + DATA_CLIENT, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_SSL, DOMAIN, + OPERATION_RESTRICTIONS_CURRENT, OPERATION_RESTRICTIONS_UNIVERSAL) _LOGGER = logging.getLogger(__name__) @@ -346,17 +348,30 @@ def __init__( """Initialize.""" self.binary_sensor_conditions = binary_sensor_conditions self.client = client + self.data = {} self.default_zone_runtime = default_zone_runtime self.device_mac = self.client.mac - self.restrictions = {} self.sensor_conditions = sensor_conditions async def async_update(self): """Update sensor/binary sensor data.""" - self.restrictions.update({ - 'current': await self.client.restrictions.current(), - 'global': await self.client.restrictions.universal() - }) + from regenmaschine.errors import RainMachineError + + tasks = { + OPERATION_RESTRICTIONS_CURRENT: self.client.restrictions.current(), + OPERATION_RESTRICTIONS_UNIVERSAL: + self.client.restrictions.universal(), + } + + results = await asyncio.gather(*tasks.values(), return_exceptions=True) + for operation, result in zip(tasks, results): + if isinstance(result, RainMachineError): + _LOGGER.error( + 'There was an error while updating %s: %s', operation, + result) + continue + + self.data[operation] = result class RainMachineEntity(Entity): diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index fcccf11e17c4f5..57dbcb551ed3bb 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -7,6 +7,7 @@ from . import ( BINARY_SENSORS, DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, + OPERATION_RESTRICTIONS_CURRENT, OPERATION_RESTRICTIONS_UNIVERSAL, SENSOR_UPDATE_TOPIC, TYPE_FREEZE, TYPE_FREEZE_PROTECTION, TYPE_HOT_DAYS, TYPE_HOURLY, TYPE_MONTH, TYPE_RAINDELAY, TYPE_RAINSENSOR, TYPE_WEEKDAY, RainMachineEntity) @@ -79,21 +80,26 @@ def update(): async def async_update(self): """Update the state.""" if self._sensor_type == TYPE_FREEZE: - self._state = self.rainmachine.restrictions['current']['freeze'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['freeze'] elif self._sensor_type == TYPE_FREEZE_PROTECTION: - self._state = self.rainmachine.restrictions['global'][ - 'freezeProtectEnabled'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_UNIVERSAL]['freezeProtectEnabled'] elif self._sensor_type == TYPE_HOT_DAYS: - self._state = self.rainmachine.restrictions['global'][ - 'hotDaysExtraWatering'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_UNIVERSAL]['hotDaysExtraWatering'] elif self._sensor_type == TYPE_HOURLY: - self._state = self.rainmachine.restrictions['current']['hourly'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['hourly'] elif self._sensor_type == TYPE_MONTH: - self._state = self.rainmachine.restrictions['current']['month'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['month'] elif self._sensor_type == TYPE_RAINDELAY: - self._state = self.rainmachine.restrictions['current']['rainDelay'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['rainDelay'] elif self._sensor_type == TYPE_RAINSENSOR: - self._state = self.rainmachine.restrictions['current'][ - 'rainSensor'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['rainSensor'] elif self._sensor_type == TYPE_WEEKDAY: - self._state = self.rainmachine.restrictions['current']['weekDay'] + self._state = self.rainmachine.data[ + OPERATION_RESTRICTIONS_CURRENT]['weekDay'] diff --git a/homeassistant/components/rainmachine/const.py b/homeassistant/components/rainmachine/const.py index 4d08a871f61c17..d142467443fb4e 100644 --- a/homeassistant/components/rainmachine/const.py +++ b/homeassistant/components/rainmachine/const.py @@ -12,4 +12,7 @@ DEFAULT_SCAN_INTERVAL = timedelta(seconds=60) DEFAULT_SSL = True +OPERATION_RESTRICTIONS_CURRENT = 'restrictions.current' +OPERATION_RESTRICTIONS_UNIVERSAL = 'restrictions.universal' + TOPIC_UPDATE = 'update_{0}' diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 08dd67755bb5aa..4894bd2ce399d8 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -5,7 +5,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import ( - DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, SENSOR_UPDATE_TOPIC, SENSORS, + DATA_CLIENT, DOMAIN as RAINMACHINE_DOMAIN, + OPERATION_RESTRICTIONS_UNIVERSAL, SENSOR_UPDATE_TOPIC, SENSORS, RainMachineEntity) _LOGGER = logging.getLogger(__name__) @@ -81,5 +82,5 @@ def update(): async def async_update(self): """Update the sensor's state.""" - self._state = self.rainmachine.restrictions['global'][ + self._state = self.rainmachine.data[OPERATION_RESTRICTIONS_UNIVERSAL][ 'freezeProtectTemp'] From b767232e5009ce7a11ea333417e9e07743317b1d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 12 Apr 2019 17:09:58 -0700 Subject: [PATCH 591/605] Only load stream when av package available (#23058) --- homeassistant/components/default_config/__init__.py | 7 ++++++- homeassistant/components/default_config/manifest.json | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/default_config/__init__.py b/homeassistant/components/default_config/__init__.py index 273513262d5f9b..23add299b2f160 100644 --- a/homeassistant/components/default_config/__init__.py +++ b/homeassistant/components/default_config/__init__.py @@ -4,9 +4,14 @@ except ImportError: av = None +from homeassistant.setup import async_setup_component + DOMAIN = 'default_config' async def async_setup(hass, config): """Initialize default configuration.""" - return True + if av is None: + return True + + return await async_setup_component(hass, 'stream', config) diff --git a/homeassistant/components/default_config/manifest.json b/homeassistant/components/default_config/manifest.json index deda9c068050f0..f52da35dc64e90 100644 --- a/homeassistant/components/default_config/manifest.json +++ b/homeassistant/components/default_config/manifest.json @@ -15,7 +15,6 @@ "mobile_app", "person", "script", - "stream", "sun", "system_health", "updater", From 3f69d0283d4302c80e945a21f6fa1b32c5f62be8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 12 Apr 2019 17:10:19 -0700 Subject: [PATCH 592/605] Convert translation helper to use async_get_integration (#23054) * Convert translation helper to use async_get_integration * Simplify after comments * Lint * Fix typing * Typo --- homeassistant/helpers/translation.py | 74 ++++++++++++---------------- tests/helpers/test_translation.py | 15 +++--- 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 63a6421d5f66ef..24e6f4f390d952 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -1,10 +1,9 @@ """Translation string lookup helpers.""" import logging -import pathlib -from typing import Any, Dict, Iterable +from typing import Any, Dict, Iterable, Optional from homeassistant import config_entries -from homeassistant.loader import get_component, get_platform, bind_hass +from homeassistant.loader import async_get_integration, bind_hass from homeassistant.util.json import load_json from .typing import HomeAssistantType @@ -30,53 +29,38 @@ def flatten(data: Dict) -> Dict[str, Any]: return recursive_flatten('', data) -def component_translation_file(hass: HomeAssistantType, component: str, - language: str) -> str: +async def component_translation_file(hass: HomeAssistantType, component: str, + language: str) -> Optional[str]: """Return the translation json file location for a component. - For component one of: - - components/light/.translations/nl.json - - components/.translations/group.nl.json + For component: + - components/hue/.translations/nl.json - For platform one of: - - components/light/.translations/hue.nl.json + For platform: - components/hue/.translations/light.nl.json - """ - is_platform = '.' in component - if not is_platform: - module = get_component(hass, component) - assert module is not None + If component is just a single file, will return None. + """ + parts = component.split('.') + domain = parts[-1] + is_platform = len(parts) == 2 - module_path = pathlib.Path(module.__file__) + integration = await async_get_integration(hass, domain) + assert integration is not None, domain - if module.__name__ == module.__package__: - # light/__init__.py - filename = '{}.json'.format(language) - else: - # group.py - filename = '{}.{}.json'.format(component, language) - - return str(module_path.parent / '.translations' / filename) - - # It's a platform - parts = component.split('.', 1) - module = get_platform(hass, *parts) - assert module is not None, component - - # Either within HA or custom_components - # Either light/hue.py or hue/light.py - module_path = pathlib.Path(module.__file__) - - # Compare to parent so we don't have to strip off `.py` - if module_path.parent.name == parts[0]: - # this is light/hue.py - filename = "{}.{}.json".format(parts[1], language) - else: - # this is hue/light.py + if is_platform: filename = "{}.{}.json".format(parts[0], language) + return str(integration.file_path / '.translations' / filename) - return str(module_path.parent / '.translations' / filename) + module = integration.get_component() + + # If it's a component that is just one file, we don't support translations + # Example custom_components/my_component.py + if module.__name__ != module.__package__: + return None + + filename = '{}.json'.format(language) + return str(integration.file_path / '.translations' / filename) def load_translations_files(translation_files: Dict[str, str]) \ @@ -130,8 +114,12 @@ async def async_get_component_resources(hass: HomeAssistantType, missing_components = components - set(translation_cache) missing_files = {} for component in missing_components: - missing_files[component] = component_translation_file( - hass, component, language) + path = await component_translation_file(hass, component, language) + # No translation available + if path is None: + translation_cache[component] = {} + else: + missing_files[component] = path # Load missing files if missing_files: diff --git a/tests/helpers/test_translation.py b/tests/helpers/test_translation.py index 34d929b285af94..ebf883bfe128a0 100644 --- a/tests/helpers/test_translation.py +++ b/tests/helpers/test_translation.py @@ -8,6 +8,7 @@ from homeassistant import config_entries import homeassistant.helpers.translation as translation from homeassistant.setup import async_setup_component +from tests.common import mock_coro @pytest.fixture @@ -52,20 +53,20 @@ async def test_component_translation_file(hass): 'test_package' }) - assert path.normpath(translation.component_translation_file( + assert path.normpath(await translation.component_translation_file( hass, 'switch.test', 'en')) == path.normpath(hass.config.path( 'custom_components', 'test', '.translations', 'switch.en.json')) - assert path.normpath(translation.component_translation_file( + assert path.normpath(await translation.component_translation_file( hass, 'switch.test_embedded', 'en')) == path.normpath(hass.config.path( 'custom_components', 'test_embedded', '.translations', 'switch.en.json')) - assert path.normpath(translation.component_translation_file( - hass, 'test_standalone', 'en')) == path.normpath(hass.config.path( - 'custom_components', '.translations', 'test_standalone.en.json')) + assert await translation.component_translation_file( + hass, 'test_standalone', 'en' + ) is None - assert path.normpath(translation.component_translation_file( + assert path.normpath(await translation.component_translation_file( hass, 'test_package', 'en')) == path.normpath(hass.config.path( 'custom_components', 'test_package', '.translations', 'en.json')) @@ -133,7 +134,7 @@ async def test_get_translations_loads_config_flows(hass, mock_config_flows): mock_config_flows.append('component1') with patch.object(translation, 'component_translation_file', - return_value='bla.json'), \ + return_value=mock_coro('bla.json')), \ patch.object(translation, 'load_translations_files', return_value={ 'component1': {'hello': 'world'}}): translations = await translation.async_get_translations(hass, 'en') From 73a473ac29328d5756a532e11e7e7ee795379023 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 12 Apr 2019 17:19:05 -0700 Subject: [PATCH 593/605] Allow aws credential skip validation (#22991) * Allow aws credential skip validation * Don't validate the auto-created default profile --- homeassistant/components/aws/__init__.py | 13 ++++-- homeassistant/components/aws/const.py | 1 + tests/components/aws/test_init.py | 54 ++++++++++++++++++++---- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/aws/__init__.py b/homeassistant/components/aws/__init__.py index 9533d2c776d157..fe57aa73db413a 100644 --- a/homeassistant/components/aws/__init__.py +++ b/homeassistant/components/aws/__init__.py @@ -20,6 +20,7 @@ CONF_REGION, CONF_SECRET_ACCESS_KEY, CONF_SERVICE, + CONF_VALIDATE, DATA_CONFIG, DATA_HASS_CONFIG, DATA_SESSIONS, @@ -34,10 +35,15 @@ vol.Inclusive(CONF_ACCESS_KEY_ID, ATTR_CREDENTIALS): cv.string, vol.Inclusive(CONF_SECRET_ACCESS_KEY, ATTR_CREDENTIALS): cv.string, vol.Exclusive(CONF_PROFILE_NAME, ATTR_CREDENTIALS): cv.string, + vol.Optional(CONF_VALIDATE, default=True): cv.boolean, } ) -DEFAULT_CREDENTIAL = [{CONF_NAME: "default", CONF_PROFILE_NAME: "default"}] +DEFAULT_CREDENTIAL = [{ + CONF_NAME: "default", + CONF_PROFILE_NAME: "default", + CONF_VALIDATE: False, +}] SUPPORTED_SERVICES = ["lambda", "sns", "sqs"] @@ -168,7 +174,8 @@ async def _validate_aws_credentials(hass, credential): else: session = aiobotocore.AioSession(loop=hass.loop) - async with session.create_client("iam", **aws_config) as client: - await client.get_user() + if credential[CONF_VALIDATE]: + async with session.create_client("iam", **aws_config) as client: + await client.get_user() return session diff --git a/homeassistant/components/aws/const.py b/homeassistant/components/aws/const.py index 4fa88566934100..4738547bdec37b 100644 --- a/homeassistant/components/aws/const.py +++ b/homeassistant/components/aws/const.py @@ -14,3 +14,4 @@ CONF_REGION = "region_name" CONF_SECRET_ACCESS_KEY = "aws_secret_access_key" CONF_SERVICE = "service" +CONF_VALIDATE = "validate" diff --git a/tests/components/aws/test_init.py b/tests/components/aws/test_init.py index 89dd9deaa0ab8d..9a0bf2ccee28a2 100644 --- a/tests/components/aws/test_init.py +++ b/tests/components/aws/test_init.py @@ -10,15 +10,19 @@ class MockAioSession: def __init__(self, *args, **kwargs): """Init a mock session.""" + self.get_user = CoroutineMock() + self.invoke = CoroutineMock() + self.publish = CoroutineMock() + self.send_message = CoroutineMock() def create_client(self, *args, **kwargs): # pylint: disable=no-self-use """Create a mocked client.""" return MagicMock( __aenter__=CoroutineMock(return_value=CoroutineMock( - get_user=CoroutineMock(), # iam - invoke=CoroutineMock(), # lambda - publish=CoroutineMock(), # sns - send_message=CoroutineMock(), # sqs + get_user=self.get_user, # iam + invoke=self.invoke, # lambda + publish=self.publish, # sns + send_message=self.send_message, # sqs )), __aexit__=CoroutineMock() ) @@ -35,7 +39,10 @@ async def test_empty_config(hass): sessions = hass.data[aws.DATA_SESSIONS] assert sessions is not None assert len(sessions) == 1 - assert isinstance(sessions.get('default'), MockAioSession) + session = sessions.get('default') + assert isinstance(session, MockAioSession) + # we don't validate auto-created default profile + session.get_user.assert_not_awaited() async def test_empty_credential(hass): @@ -55,7 +62,8 @@ async def test_empty_credential(hass): sessions = hass.data[aws.DATA_SESSIONS] assert sessions is not None assert len(sessions) == 1 - assert isinstance(sessions.get('default'), MockAioSession) + session = sessions.get('default') + assert isinstance(session, MockAioSession) assert hass.services.has_service('notify', 'new_lambda_test') is True await hass.services.async_call( @@ -64,6 +72,7 @@ async def test_empty_credential(hass): {'message': 'test', 'target': 'ARN'}, blocking=True ) + session.invoke.assert_awaited_once() async def test_profile_credential(hass): @@ -88,7 +97,8 @@ async def test_profile_credential(hass): sessions = hass.data[aws.DATA_SESSIONS] assert sessions is not None assert len(sessions) == 1 - assert isinstance(sessions.get('test'), MockAioSession) + session = sessions.get('test') + assert isinstance(session, MockAioSession) assert hass.services.has_service('notify', 'sns_test') is True await hass.services.async_call( @@ -97,6 +107,7 @@ async def test_profile_credential(hass): {'title': 'test', 'message': 'test', 'target': 'ARN'}, blocking=True ) + session.publish.assert_awaited_once() async def test_access_key_credential(hass): @@ -128,7 +139,8 @@ async def test_access_key_credential(hass): sessions = hass.data[aws.DATA_SESSIONS] assert sessions is not None assert len(sessions) == 2 - assert isinstance(sessions.get('key'), MockAioSession) + session = sessions.get('key') + assert isinstance(session, MockAioSession) assert hass.services.has_service('notify', 'sns_test') is True await hass.services.async_call( @@ -137,6 +149,7 @@ async def test_access_key_credential(hass): {'title': 'test', 'message': 'test', 'target': 'ARN'}, blocking=True ) + session.publish.assert_awaited_once() async def test_notify_credential(hass): @@ -197,3 +210,28 @@ async def test_notify_credential_profile(hass): {'message': 'test', 'target': 'ARN'}, blocking=True ) + + +async def test_credential_skip_validate(hass): + """Test credential can skip validate.""" + with async_patch('aiobotocore.AioSession', new=MockAioSession): + await async_setup_component(hass, 'aws', { + 'aws': { + 'credentials': [ + { + 'name': 'key', + 'aws_access_key_id': 'not-valid', + 'aws_secret_access_key': 'dont-care', + 'validate': False + }, + ], + } + }) + await hass.async_block_till_done() + + sessions = hass.data[aws.DATA_SESSIONS] + assert sessions is not None + assert len(sessions) == 1 + session = sessions.get('key') + assert isinstance(session, MockAioSession) + session.get_user.assert_not_awaited() From 18cf8275b8f4576218481b69c8527d681c042946 Mon Sep 17 00:00:00 2001 From: Josef Schlehofer Date: Sat, 13 Apr 2019 11:29:44 +0200 Subject: [PATCH 594/605] Upgrade python-slugify to 3.0.2 (#22997) --- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 68c4a627083ed3..3bef086d70a148 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -8,7 +8,7 @@ jinja2>=2.10 PyJWT==1.7.1 cryptography==2.6.1 pip>=8.0.3 -python-slugify==1.2.6 +python-slugify==3.0.2 pytz>=2019.01 pyyaml>=3.13,<4 requests==2.21.0 diff --git a/requirements_all.txt b/requirements_all.txt index ee7503811c599a..0a513debf9ea2c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -9,7 +9,7 @@ jinja2>=2.10 PyJWT==1.7.1 cryptography==2.6.1 pip>=8.0.3 -python-slugify==1.2.6 +python-slugify==3.0.2 pytz>=2019.01 pyyaml>=3.13,<4 requests==2.21.0 diff --git a/setup.py b/setup.py index 9be6bbd3a310ed..6f67f93d3e2e16 100755 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ # PyJWT has loose dependency. We want the latest one. 'cryptography==2.6.1', 'pip>=8.0.3', - 'python-slugify==1.2.6', + 'python-slugify==3.0.2', 'pytz>=2019.01', 'pyyaml>=3.13,<4', 'requests==2.21.0', From 2f17529f2833b69994870650430521b270bd69eb Mon Sep 17 00:00:00 2001 From: c-soft Date: Sat, 13 Apr 2019 14:24:12 +0200 Subject: [PATCH 595/605] Add Satel_integra switchable outputs and multiple partitions (#21992) * Added editable outputs and multiple zones. * Updated requirements_all.txt * Linter fixes. * Post-review changes * Fixed too many lines separation error * Passing satel controller as parameter to entities. * Fixed linter error. * Fixed forgotten requirements update. * Fixed satel_integra version (again!?!) * Fixed manifest.json. * Fixed passing non-serializable controller * Removed unnecessary isinstance check. * Post review changes --- .../components/satel_integra/__init__.py | 63 +++++++++--- .../satel_integra/alarm_control_panel.py | 47 +++++---- .../components/satel_integra/binary_sensor.py | 19 ++-- .../components/satel_integra/manifest.json | 2 +- .../components/satel_integra/switch.py | 97 +++++++++++++++++++ requirements_all.txt | 2 +- 6 files changed, 186 insertions(+), 44 deletions(-) create mode 100644 homeassistant/components/satel_integra/switch.py diff --git a/homeassistant/components/satel_integra/__init__.py b/homeassistant/components/satel_integra/__init__.py index 2aae9ea8dd9a10..ea1029e4fe09d0 100644 --- a/homeassistant/components/satel_integra/__init__.py +++ b/homeassistant/components/satel_integra/__init__.py @@ -1,9 +1,10 @@ """Support for Satel Integra devices.""" +import collections import logging import voluptuous as vol -from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_HOST +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT from homeassistant.core import callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.discovery import async_load_platform @@ -21,13 +22,14 @@ DATA_SATEL = 'satel_integra' -CONF_DEVICE_PORT = 'port' -CONF_DEVICE_PARTITION = 'partition' +CONF_DEVICE_CODE = 'code' +CONF_DEVICE_PARTITIONS = 'partitions' CONF_ARM_HOME_MODE = 'arm_home_mode' CONF_ZONE_NAME = 'name' CONF_ZONE_TYPE = 'type' CONF_ZONES = 'zones' CONF_OUTPUTS = 'outputs' +CONF_SWITCHABLE_OUTPUTS = 'switchable_outputs' ZONES = 'zones' @@ -42,20 +44,38 @@ ZONE_SCHEMA = vol.Schema({ vol.Required(CONF_ZONE_NAME): cv.string, vol.Optional(CONF_ZONE_TYPE, default=DEFAULT_ZONE_TYPE): cv.string}) +EDITABLE_OUTPUT_SCHEMA = vol.Schema({vol.Required(CONF_ZONE_NAME): cv.string}) +PARTITION_SCHEMA = vol.Schema( + {vol.Required(CONF_ZONE_NAME): cv.string, + vol.Optional(CONF_ARM_HOME_MODE, default=DEFAULT_CONF_ARM_HOME_MODE): + vol.In([1, 2, 3]), + } + ) + + +def is_alarm_code_necessary(value): + """Check if alarm code must be configured.""" + if value.get(CONF_SWITCHABLE_OUTPUTS) and CONF_DEVICE_CODE not in value: + raise vol.Invalid('You need to specify alarm ' + ' code to use switchable_outputs') + + return value + CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ + DOMAIN: vol.All({ vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_DEVICE_PARTITION, - default=DEFAULT_DEVICE_PARTITION): cv.positive_int, - vol.Optional(CONF_ARM_HOME_MODE, - default=DEFAULT_CONF_ARM_HOME_MODE): vol.In([1, 2, 3]), + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_DEVICE_CODE): cv.string, + vol.Optional(CONF_DEVICE_PARTITIONS, + default={}): {vol.Coerce(int): PARTITION_SCHEMA}, vol.Optional(CONF_ZONES, default={}): {vol.Coerce(int): ZONE_SCHEMA}, vol.Optional(CONF_OUTPUTS, default={}): {vol.Coerce(int): ZONE_SCHEMA}, - }), + vol.Optional(CONF_SWITCHABLE_OUTPUTS, + default={}): {vol.Coerce(int): EDITABLE_OUTPUT_SCHEMA}, + }, is_alarm_code_necessary), }, extra=vol.ALLOW_EXTRA) @@ -65,13 +85,20 @@ async def async_setup(hass, config): zones = conf.get(CONF_ZONES) outputs = conf.get(CONF_OUTPUTS) + switchable_outputs = conf.get(CONF_SWITCHABLE_OUTPUTS) host = conf.get(CONF_HOST) - port = conf.get(CONF_DEVICE_PORT) - partition = conf.get(CONF_DEVICE_PARTITION) + port = conf.get(CONF_PORT) + partitions = conf.get(CONF_DEVICE_PARTITIONS) from satel_integra.satel_integra import AsyncSatel - controller = AsyncSatel(host, port, hass.loop, zones, outputs, partition) + monitored_outputs = collections.OrderedDict( + list(outputs.items()) + + list(switchable_outputs.items()) + ) + + controller = AsyncSatel(host, port, hass.loop, + zones, monitored_outputs, partitions) hass.data[DATA_SATEL] = controller @@ -94,7 +121,15 @@ async def _close(): hass.async_create_task( async_load_platform(hass, 'binary_sensor', DOMAIN, - {CONF_ZONES: zones, CONF_OUTPUTS: outputs}, config) + {CONF_ZONES: zones, + CONF_OUTPUTS: outputs}, config) + ) + + hass.async_create_task( + async_load_platform(hass, 'switch', DOMAIN, + {CONF_SWITCHABLE_OUTPUTS: switchable_outputs, + CONF_DEVICE_CODE: conf.get(CONF_DEVICE_CODE)}, + config) ) @callback diff --git a/homeassistant/components/satel_integra/alarm_control_panel.py b/homeassistant/components/satel_integra/alarm_control_panel.py index 02e683bac5a164..a896d7e8061cf9 100644 --- a/homeassistant/components/satel_integra/alarm_control_panel.py +++ b/homeassistant/components/satel_integra/alarm_control_panel.py @@ -11,7 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import ( - CONF_ARM_HOME_MODE, CONF_DEVICE_PARTITION, DATA_SATEL, + CONF_ARM_HOME_MODE, CONF_DEVICE_PARTITIONS, DATA_SATEL, CONF_ZONE_NAME, SIGNAL_PANEL_MESSAGE) _LOGGER = logging.getLogger(__name__) @@ -23,23 +23,34 @@ async def async_setup_platform( if not discovery_info: return - device = SatelIntegraAlarmPanel( - "Alarm Panel", - discovery_info.get(CONF_ARM_HOME_MODE), - discovery_info.get(CONF_DEVICE_PARTITION)) + configured_partitions = discovery_info[CONF_DEVICE_PARTITIONS] + controller = hass.data[DATA_SATEL] - async_add_entities([device]) + devices = [] + + for partition_num, device_config_data in configured_partitions.items(): + zone_name = device_config_data[CONF_ZONE_NAME] + arm_home_mode = device_config_data.get(CONF_ARM_HOME_MODE) + device = SatelIntegraAlarmPanel( + controller, + zone_name, + arm_home_mode, + partition_num) + devices.append(device) + + async_add_entities(devices) class SatelIntegraAlarmPanel(alarm.AlarmControlPanel): """Representation of an AlarmDecoder-based alarm panel.""" - def __init__(self, name, arm_home_mode, partition_id): + def __init__(self, controller, name, arm_home_mode, partition_id): """Initialize the alarm panel.""" self._name = name self._state = None self._arm_home_mode = arm_home_mode self._partition_id = partition_id + self._satel = controller async def async_added_to_hass(self): """Update alarm status and register callbacks for future updates.""" @@ -66,13 +77,13 @@ def _read_alarm_state(self): # Default - disarmed: hass_alarm_status = STATE_ALARM_DISARMED - satel_controller = self.hass.data[DATA_SATEL] - if not satel_controller.connected: + if not self._satel.connected: return None state_map = OrderedDict([ (AlarmState.TRIGGERED, STATE_ALARM_TRIGGERED), (AlarmState.TRIGGERED_FIRE, STATE_ALARM_TRIGGERED), + (AlarmState.ENTRY_TIME, STATE_ALARM_PENDING), (AlarmState.ARMED_MODE3, STATE_ALARM_ARMED_HOME), (AlarmState.ARMED_MODE2, STATE_ALARM_ARMED_HOME), (AlarmState.ARMED_MODE1, STATE_ALARM_ARMED_HOME), @@ -80,13 +91,11 @@ def _read_alarm_state(self): (AlarmState.EXIT_COUNTDOWN_OVER_10, STATE_ALARM_PENDING), (AlarmState.EXIT_COUNTDOWN_UNDER_10, STATE_ALARM_PENDING) ]) - _LOGGER.debug("State map of Satel: %s", - satel_controller.partition_states) + _LOGGER.debug("State map of Satel: %s", self._satel.partition_states) for satel_state, ha_state in state_map.items(): - if satel_state in satel_controller.partition_states and\ - self._partition_id in\ - satel_controller.partition_states[satel_state]: + if satel_state in self._satel.partition_states and\ + self._partition_id in self._satel.partition_states[satel_state]: hass_alarm_status = ha_state break @@ -122,24 +131,24 @@ async def async_alarm_disarm(self, code=None): _LOGGER.debug("Disarming, self._state: %s", self._state) - await self.hass.data[DATA_SATEL].disarm(code) + await self._satel.disarm(code, [self._partition_id]) if clear_alarm_necessary: # Wait 1s before clearing the alarm await asyncio.sleep(1) - await self.hass.data[DATA_SATEL].clear_alarm(code) + await self._satel.clear_alarm(code, [self._partition_id]) async def async_alarm_arm_away(self, code=None): """Send arm away command.""" _LOGGER.debug("Arming away") if code: - await self.hass.data[DATA_SATEL].arm(code) + await self._satel.arm(code, [self._partition_id]) async def async_alarm_arm_home(self, code=None): """Send arm home command.""" _LOGGER.debug("Arming home") if code: - await self.hass.data[DATA_SATEL].arm( - code, self._arm_home_mode) + await self._satel.arm( + code, [self._partition_id], self._arm_home_mode) diff --git a/homeassistant/components/satel_integra/binary_sensor.py b/homeassistant/components/satel_integra/binary_sensor.py index faef1a6f45e51f..ebaf11f0766758 100644 --- a/homeassistant/components/satel_integra/binary_sensor.py +++ b/homeassistant/components/satel_integra/binary_sensor.py @@ -6,8 +6,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import ( - CONF_OUTPUTS, CONF_ZONE_NAME, CONF_ZONE_TYPE, CONF_ZONES, DATA_SATEL, - SIGNAL_OUTPUTS_UPDATED, SIGNAL_ZONES_UPDATED) + CONF_OUTPUTS, CONF_ZONE_NAME, CONF_ZONE_TYPE, CONF_ZONES, + SIGNAL_OUTPUTS_UPDATED, SIGNAL_ZONES_UPDATED, DATA_SATEL) _LOGGER = logging.getLogger(__name__) @@ -19,6 +19,7 @@ async def async_setup_platform( return configured_zones = discovery_info[CONF_ZONES] + controller = hass.data[DATA_SATEL] devices = [] @@ -26,7 +27,7 @@ async def async_setup_platform( zone_type = device_config_data[CONF_ZONE_TYPE] zone_name = device_config_data[CONF_ZONE_NAME] device = SatelIntegraBinarySensor( - zone_num, zone_name, zone_type, SIGNAL_ZONES_UPDATED) + controller, zone_num, zone_name, zone_type, SIGNAL_ZONES_UPDATED) devices.append(device) configured_outputs = discovery_info[CONF_OUTPUTS] @@ -35,7 +36,7 @@ async def async_setup_platform( zone_type = device_config_data[CONF_ZONE_TYPE] zone_name = device_config_data[CONF_ZONE_NAME] device = SatelIntegraBinarySensor( - zone_num, zone_name, zone_type, SIGNAL_OUTPUTS_UPDATED) + controller, zone_num, zone_name, zone_type, SIGNAL_OUTPUTS_UPDATED) devices.append(device) async_add_entities(devices) @@ -44,25 +45,25 @@ async def async_setup_platform( class SatelIntegraBinarySensor(BinarySensorDevice): """Representation of an Satel Integra binary sensor.""" - def __init__(self, device_number, device_name, zone_type, react_to_signal): + def __init__(self, controller, device_number, device_name, + zone_type, react_to_signal): """Initialize the binary_sensor.""" self._device_number = device_number self._name = device_name self._zone_type = zone_type self._state = 0 self._react_to_signal = react_to_signal + self._satel = controller async def async_added_to_hass(self): """Register callbacks.""" if self._react_to_signal == SIGNAL_OUTPUTS_UPDATED: - if self._device_number in\ - self.hass.data[DATA_SATEL].violated_outputs: + if self._device_number in self._satel.violated_outputs: self._state = 1 else: self._state = 0 else: - if self._device_number in\ - self.hass.data[DATA_SATEL].violated_zones: + if self._device_number in self._satel.violated_zones: self._state = 1 else: self._state = 0 diff --git a/homeassistant/components/satel_integra/manifest.json b/homeassistant/components/satel_integra/manifest.json index 8df19ed90de935..ae56b54ce18bbf 100644 --- a/homeassistant/components/satel_integra/manifest.json +++ b/homeassistant/components/satel_integra/manifest.json @@ -3,7 +3,7 @@ "name": "Satel integra", "documentation": "https://www.home-assistant.io/components/satel_integra", "requirements": [ - "satel_integra==0.3.2" + "satel_integra==0.3.4" ], "dependencies": [], "codeowners": [] diff --git a/homeassistant/components/satel_integra/switch.py b/homeassistant/components/satel_integra/switch.py new file mode 100644 index 00000000000000..77c07569fa4ec4 --- /dev/null +++ b/homeassistant/components/satel_integra/switch.py @@ -0,0 +1,97 @@ +"""Support for Satel Integra modifiable outputs represented as switches.""" +import logging + +from homeassistant.components.switch import SwitchDevice +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import ( + CONF_DEVICE_CODE, CONF_SWITCHABLE_OUTPUTS, CONF_ZONE_NAME, + SIGNAL_OUTPUTS_UPDATED, DATA_SATEL) + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['satel_integra'] + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the Satel Integra switch devices.""" + if not discovery_info: + return + + configured_zones = discovery_info[CONF_SWITCHABLE_OUTPUTS] + controller = hass.data[DATA_SATEL] + + devices = [] + + for zone_num, device_config_data in configured_zones.items(): + zone_name = device_config_data[CONF_ZONE_NAME] + + device = SatelIntegraSwitch( + controller, zone_num, zone_name, discovery_info[CONF_DEVICE_CODE]) + devices.append(device) + + async_add_entities(devices) + + +class SatelIntegraSwitch(SwitchDevice): + """Representation of an Satel switch.""" + + def __init__(self, controller, device_number, device_name, code): + """Initialize the binary_sensor.""" + self._device_number = device_number + self._name = device_name + self._state = False + self._code = code + self._satel = controller + + async def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_OUTPUTS_UPDATED, self._devices_updated) + + @callback + def _devices_updated(self, zones): + """Update switch state, if needed.""" + _LOGGER.debug("Update switch name: %s zones: %s", self._name, zones) + if self._device_number in zones: + new_state = self._read_state() + _LOGGER.debug("New state: %s", new_state) + if new_state != self._state: + self._state = new_state + self.async_schedule_update_ha_state() + + async def async_turn_on(self, **kwargs): + """Turn the device on.""" + _LOGGER.debug("Switch: %s status: %s," + " turning on", self._name, self._state) + await self._satel.set_output(self._code, self._device_number, True) + self.async_schedule_update_ha_state() + + async def async_turn_off(self, **kwargs): + """Turn the device off.""" + _LOGGER.debug("Switch name: %s status: %s," + " turning off", self._name, self._state) + await self._satel.set_output(self._code, self._device_number, False) + self.async_schedule_update_ha_state() + + @property + def is_on(self): + """Return true if device is on.""" + self._state = self._read_state() + return self._state + + def _read_state(self): + """Read state of the device.""" + return self._device_number in self._satel.violated_outputs + + @property + def name(self): + """Return the name of the switch.""" + return self._name + + @property + def should_poll(self): + """Don't poll.""" + return False diff --git a/requirements_all.txt b/requirements_all.txt index 0a513debf9ea2c..c37cceb1a123a3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1534,7 +1534,7 @@ rxv==0.6.0 samsungctl[websocket]==0.7.1 # homeassistant.components.satel_integra -satel_integra==0.3.2 +satel_integra==0.3.4 # homeassistant.components.deutsche_bahn schiene==0.23 From 479511ee425de3d32940a58a39417646b4b839ac Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Sat, 13 Apr 2019 12:57:22 -0400 Subject: [PATCH 596/605] Bugfix ZHA device_removed() handler. (#23074) --- homeassistant/components/zha/core/gateway.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 83013b7bdf75fe..17c7c6f878f193 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -145,14 +145,14 @@ def device_left(self, device): def device_removed(self, device): """Handle device being removed from the network.""" - device = self._devices.pop(device.ieee, None) + zha_device = self._devices.pop(device.ieee, None) self._device_registry.pop(device.ieee, None) - if device is not None: - device_info = async_get_device_info(self._hass, device) - self._hass.async_create_task(device.async_unsub_dispatcher()) + if zha_device is not None: + device_info = async_get_device_info(self._hass, zha_device) + self._hass.async_create_task(zha_device.async_unsub_dispatcher()) async_dispatcher_send( self._hass, - "{}_{}".format(SIGNAL_REMOVE, str(device.ieee)) + "{}_{}".format(SIGNAL_REMOVE, str(zha_device.ieee)) ) if device_info is not None: async_dispatcher_send( From 2527731865b2be5944f8fed39872a5a4283a4f21 Mon Sep 17 00:00:00 2001 From: Penny Wood Date: Sun, 14 Apr 2019 01:48:40 +0800 Subject: [PATCH 597/605] Fix websocket connection sensor (#22923) * Fix for #22890 * Singleton count --- .../components/websocket_api/const.py | 3 ++ .../components/websocket_api/http.py | 6 +++- .../components/websocket_api/sensor.py | 29 +++++++++---------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/websocket_api/const.py b/homeassistant/components/websocket_api/const.py index 4c3e0d564dc8fb..53ca680c4c944a 100644 --- a/homeassistant/components/websocket_api/const.py +++ b/homeassistant/components/websocket_api/const.py @@ -24,3 +24,6 @@ # Event types SIGNAL_WEBSOCKET_CONNECTED = 'websocket_connected' SIGNAL_WEBSOCKET_DISCONNECTED = 'websocket_disconnected' + +# Data used to store the current connection list +DATA_CONNECTIONS = DOMAIN + '.connections' diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index b51e5d5699d9e8..0fc446390b733a 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -15,7 +15,8 @@ from .const import ( MAX_PENDING_MSG, CANCELLATION_ERRORS, URL, ERR_UNKNOWN_ERROR, - SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED) + SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED, + DATA_CONNECTIONS) from .auth import AuthPhase, auth_required_message from .error import Disconnect from .messages import error_message @@ -145,6 +146,8 @@ def handle_hass_stop(event): self._logger.debug("Received %s", msg) connection = await auth.async_handle(msg) + self.hass.data[DATA_CONNECTIONS] = \ + self.hass.data.get(DATA_CONNECTIONS, 0) + 1 self.hass.helpers.dispatcher.async_dispatcher_send( SIGNAL_WEBSOCKET_CONNECTED) @@ -197,6 +200,7 @@ def handle_hass_stop(event): else: self._logger.warning("Disconnected: %s", disconnect_warn) + self.hass.data[DATA_CONNECTIONS] -= 1 self.hass.helpers.dispatcher.async_dispatcher_send( SIGNAL_WEBSOCKET_DISCONNECTED) diff --git a/homeassistant/components/websocket_api/sensor.py b/homeassistant/components/websocket_api/sensor.py index fd9108c0513c17..b43e356b9cedd0 100644 --- a/homeassistant/components/websocket_api/sensor.py +++ b/homeassistant/components/websocket_api/sensor.py @@ -3,7 +3,9 @@ from homeassistant.core import callback from homeassistant.helpers.entity import Entity -from .const import SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED +from .const import ( + SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED, + DATA_CONNECTIONS) async def async_setup_platform( @@ -11,12 +13,6 @@ async def async_setup_platform( """Set up the API streams platform.""" entity = APICount() - # pylint: disable=protected-access - hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_WEBSOCKET_CONNECTED, entity._increment) - hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_WEBSOCKET_DISCONNECTED, entity._decrement) - async_add_entities([entity]) @@ -25,7 +21,15 @@ class APICount(Entity): def __init__(self): """Initialize the API count.""" - self.count = 0 + self.count = None + + async def async_added_to_hass(self): + """Added to hass.""" + self.hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_CONNECTED, self._update_count) + self.hass.helpers.dispatcher.async_dispatcher_connect( + SIGNAL_WEBSOCKET_DISCONNECTED, self._update_count) + self._update_count() @property def name(self): @@ -43,11 +47,6 @@ def unit_of_measurement(self): return "clients" @callback - def _increment(self): - self.count += 1 - self.async_schedule_update_ha_state() - - @callback - def _decrement(self): - self.count -= 1 + def _update_count(self): + self.count = self.hass.data.get(DATA_CONNECTIONS, 0) self.async_schedule_update_ha_state() From 7a6950fd72edf6d716de5ce8b7f0c5bde573cacb Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sat, 13 Apr 2019 19:58:12 +0200 Subject: [PATCH 598/605] Validate data packet format on config validation (#23062) --- homeassistant/components/broadlink/switch.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/broadlink/switch.py b/homeassistant/components/broadlink/switch.py index e79d78774e99e0..d1b769e3d834a8 100644 --- a/homeassistant/components/broadlink/switch.py +++ b/homeassistant/components/broadlink/switch.py @@ -1,5 +1,4 @@ """Support for Broadlink RM devices.""" -from base64 import b64decode import binascii from datetime import timedelta import logging @@ -15,7 +14,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util import Throttle, slugify -from . import async_setup_service +from . import async_setup_service, data_packet _LOGGER = logging.getLogger(__name__) @@ -35,8 +34,8 @@ SWITCH_TYPES = RM_TYPES + SP1_TYPES + SP2_TYPES + MP1_TYPES SWITCH_SCHEMA = vol.Schema({ - vol.Optional(CONF_COMMAND_OFF): cv.string, - vol.Optional(CONF_COMMAND_ON): cv.string, + vol.Optional(CONF_COMMAND_OFF): data_packet, + vol.Optional(CONF_COMMAND_ON): data_packet, vol.Optional(CONF_FRIENDLY_NAME): cv.string, }) @@ -124,8 +123,8 @@ def __init__(self, name, friendly_name, device, command_on, command_off): self.entity_id = ENTITY_ID_FORMAT.format(slugify(name)) self._name = friendly_name self._state = False - self._command_on = b64decode(command_on) if command_on else None - self._command_off = b64decode(command_off) if command_off else None + self._command_on = command_on + self._command_off = command_off self._device = device @property From fc481133e7dca1df11d874a0340d2fef959762aa Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 13 Apr 2019 13:54:29 -0600 Subject: [PATCH 599/605] Create decorator to check service permissions (#22667) * Create decorator to check service permissions * Typing * Linting * Member comments * Linting * Member comments * Updated import * Owner comments * Linting * Linting * More work * Fixed tests * Removed service helper tests in RainMachine * Linting * Owner comments * Linting * Owner comments Co-Authored-By: bachya --- .../components/rainmachine/__init__.py | 68 +++-------- homeassistant/helpers/service.py | 48 +++++++- tests/components/rainmachine/conftest.py | 23 ---- .../rainmachine/test_service_permissions.py | 41 ------- tests/helpers/test_service.py | 115 ++++++++++++++++++ 5 files changed, 177 insertions(+), 118 deletions(-) delete mode 100644 tests/components/rainmachine/conftest.py delete mode 100644 tests/components/rainmachine/test_service_permissions.py diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index ed46f50ec3bf4a..8c058557fc15c6 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -2,22 +2,20 @@ import asyncio import logging from datetime import timedelta -from functools import wraps import voluptuous as vol -from homeassistant.auth.permissions.const import POLICY_CONTROL from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_BINARY_SENSORS, CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, CONF_SENSORS, CONF_SSL, CONF_MONITORED_CONDITIONS, CONF_SWITCHES) -from homeassistant.exceptions import ( - ConfigEntryNotReady, Unauthorized, UnknownUser) +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.service import verify_domain_control from .config_flow import configured_instances from .const import ( @@ -131,44 +129,6 @@ }, extra=vol.ALLOW_EXTRA) -def _check_valid_user(hass): - """Ensure the user of a service call has proper permissions.""" - def decorator(service): - """Decorate.""" - @wraps(service) - async def check_permissions(call): - """Check user permission and raise before call if unauthorized.""" - if not call.context.user_id: - return - - user = await hass.auth.async_get_user(call.context.user_id) - if user is None: - raise UnknownUser( - context=call.context, - permission=POLICY_CONTROL - ) - - # RainMachine services don't interact with specific entities. - # Therefore, we examine _all_ RainMachine entities and if the user - # has permission to control _any_ of them, the user has permission - # to call the service: - en_reg = await hass.helpers.entity_registry.async_get_registry() - rainmachine_entities = [ - entity.entity_id for entity in en_reg.entities.values() - if entity.platform == DOMAIN - ] - for entity_id in rainmachine_entities: - if user.permissions.check_entity(entity_id, POLICY_CONTROL): - return await service(call) - - raise Unauthorized( - context=call.context, - permission=POLICY_CONTROL, - ) - return check_permissions - return decorator - - async def async_setup(hass, config): """Set up the RainMachine component.""" hass.data[DOMAIN] = {} @@ -198,6 +158,8 @@ async def async_setup_entry(hass, config_entry): from regenmaschine import login from regenmaschine.errors import RainMachineError + _verify_domain_control = verify_domain_control(hass, DOMAIN) + websession = aiohttp_client.async_get_clientsession(hass) try: @@ -238,69 +200,69 @@ async def refresh(event_time): refresh, timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])) - @_check_valid_user(hass) + @_verify_domain_control async def disable_program(call): """Disable a program.""" await rainmachine.client.programs.disable( call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def disable_zone(call): """Disable a zone.""" await rainmachine.client.zones.disable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def enable_program(call): """Enable a program.""" await rainmachine.client.programs.enable(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def enable_zone(call): """Enable a zone.""" await rainmachine.client.zones.enable(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def pause_watering(call): """Pause watering for a set number of seconds.""" await rainmachine.client.watering.pause_all(call.data[CONF_SECONDS]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def start_program(call): """Start a particular program.""" await rainmachine.client.programs.start(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def start_zone(call): """Start a particular zone for a certain amount of time.""" await rainmachine.client.zones.start( call.data[CONF_ZONE_ID], call.data[CONF_ZONE_RUN_TIME]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def stop_all(call): """Stop all watering.""" await rainmachine.client.watering.stop_all() async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def stop_program(call): """Stop a program.""" await rainmachine.client.programs.stop(call.data[CONF_PROGRAM_ID]) async_dispatcher_send(hass, PROGRAM_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def stop_zone(call): """Stop a zone.""" await rainmachine.client.zones.stop(call.data[CONF_ZONE_ID]) async_dispatcher_send(hass, ZONE_UPDATE_TOPIC) - @_check_valid_user(hass) + @_verify_domain_control async def unpause_watering(call): """Unpause watering.""" await rainmachine.client.watering.unpause_all() diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index f5de2419fd4416..58f127ac707e3a 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -6,7 +6,7 @@ import voluptuous as vol -from homeassistant.auth.permissions.const import POLICY_CONTROL +from homeassistant.auth.permissions.const import CAT_ENTITIES, POLICY_CONTROL from homeassistant.const import ( ATTR_ENTITY_ID, ENTITY_MATCH_ALL, ATTR_AREA_ID) import homeassistant.core as ha @@ -19,6 +19,8 @@ from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.helpers.typing import HomeAssistantType +from .typing import HomeAssistantType + CONF_SERVICE = 'service' CONF_SERVICE_TEMPLATE = 'service_template' CONF_SERVICE_ENTITY_ID = 'entity_id' @@ -369,3 +371,47 @@ async def admin_handler(call): hass.services.async_register( domain, service, admin_handler, schema ) + + +@bind_hass +@ha.callback +def verify_domain_control(hass: HomeAssistantType, domain: str) -> Callable: + """Ensure permission to access any entity under domain in service call.""" + def decorator(service_handler: Callable) -> Callable: + """Decorate.""" + if not asyncio.iscoroutinefunction(service_handler): + raise HomeAssistantError( + 'Can only decorate async functions.') + + async def check_permissions(call): + """Check user permission and raise before call if unauthorized.""" + if not call.context.user_id: + return await service_handler(call) + + user = await hass.auth.async_get_user(call.context.user_id) + if user is None: + raise UnknownUser( + context=call.context, + permission=POLICY_CONTROL, + user_id=call.context.user_id) + + reg = await hass.helpers.entity_registry.async_get_registry() + entities = [ + entity.entity_id for entity in reg.entities.values() + if entity.platform == domain + ] + + for entity_id in entities: + if user.permissions.check_entity(entity_id, POLICY_CONTROL): + return await service_handler(call) + + raise Unauthorized( + context=call.context, + permission=POLICY_CONTROL, + user_id=call.context.user_id, + perm_category=CAT_ENTITIES + ) + + return check_permissions + + return decorator diff --git a/tests/components/rainmachine/conftest.py b/tests/components/rainmachine/conftest.py deleted file mode 100644 index fdc81151995601..00000000000000 --- a/tests/components/rainmachine/conftest.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Configuration for Rainmachine tests.""" -import pytest - -from homeassistant.components.rainmachine.const import DOMAIN -from homeassistant.const import ( - CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, CONF_SSL) - -from tests.common import MockConfigEntry - - -@pytest.fixture(name="config_entry") -def config_entry_fixture(): - """Create a mock RainMachine config entry.""" - return MockConfigEntry( - domain=DOMAIN, - title='192.168.1.101', - data={ - CONF_IP_ADDRESS: '192.168.1.101', - CONF_PASSWORD: '12345', - CONF_PORT: 8080, - CONF_SSL: True, - CONF_SCAN_INTERVAL: 60, - }) diff --git a/tests/components/rainmachine/test_service_permissions.py b/tests/components/rainmachine/test_service_permissions.py deleted file mode 100644 index caa84337517c00..00000000000000 --- a/tests/components/rainmachine/test_service_permissions.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Define tests for permissions on RainMachine service calls.""" -import asynctest -import pytest - -from homeassistant.components.rainmachine.const import DOMAIN -from homeassistant.core import Context -from homeassistant.exceptions import Unauthorized, UnknownUser -from homeassistant.setup import async_setup_component - -from tests.common import mock_coro - - -async def setup_platform(hass, config_entry): - """Set up the media player platform for testing.""" - with asynctest.mock.patch('regenmaschine.login') as mock_login: - mock_client = mock_login.return_value - mock_client.restrictions.current.return_value = mock_coro() - mock_client.restrictions.universal.return_value = mock_coro() - config_entry.add_to_hass(hass) - assert await async_setup_component(hass, DOMAIN) - await hass.async_block_till_done() - - -async def test_services_authorization( - hass, config_entry, hass_read_only_user): - """Test that a RainMachine service is halted on incorrect permissions.""" - await setup_platform(hass, config_entry) - - with pytest.raises(UnknownUser): - await hass.services.async_call( - 'rainmachine', - 'unpause_watering', {}, - blocking=True, - context=Context(user_id='fake_user_id')) - - with pytest.raises(Unauthorized): - await hass.services.async_call( - 'rainmachine', - 'unpause_watering', {}, - blocking=True, - context=Context(user_id=hass_read_only_user.id)) diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 231ffddff3095c..e6f4b15457e455 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -38,12 +38,14 @@ def mock_entities(): available=True, should_poll=False, supported_features=1, + platform='test_domain', ) living_room = Mock( entity_id='light.living_room', available=True, should_poll=False, supported_features=0, + platform='test_domain', ) entities = OrderedDict() entities[kitchen.entity_id] = kitchen @@ -461,3 +463,116 @@ async def mock_service(call): )) assert len(calls) == 1 assert calls[0].context.user_id == hass_admin_user.id + + +async def test_domain_control_not_async(hass, mock_entities): + """Test domain verification in a service call with an unknown user.""" + calls = [] + + def mock_service_log(call): + """Define a protected service.""" + calls.append(call) + + with pytest.raises(exceptions.HomeAssistantError): + hass.helpers.service.verify_domain_control( + 'test_domain')(mock_service_log) + + +async def test_domain_control_unknown(hass, mock_entities): + """Test domain verification in a service call with an unknown user.""" + calls = [] + + async def mock_service_log(call): + """Define a protected service.""" + calls.append(call) + + with patch('homeassistant.helpers.entity_registry.async_get_registry', + return_value=mock_coro(Mock(entities=mock_entities))): + protected_mock_service = hass.helpers.service.verify_domain_control( + 'test_domain')(mock_service_log) + + hass.services.async_register( + 'test_domain', 'test_service', protected_mock_service, schema=None) + + with pytest.raises(exceptions.UnknownUser): + await hass.services.async_call( + 'test_domain', + 'test_service', {}, + blocking=True, + context=ha.Context(user_id='fake_user_id')) + assert len(calls) == 0 + + +async def test_domain_control_unauthorized( + hass, hass_read_only_user, mock_entities): + """Test domain verification in a service call with an unauthorized user.""" + calls = [] + + async def mock_service_log(call): + """Define a protected service.""" + calls.append(call) + + with patch('homeassistant.helpers.entity_registry.async_get_registry', + return_value=mock_coro(Mock(entities=mock_entities))): + protected_mock_service = hass.helpers.service.verify_domain_control( + 'test_domain')(mock_service_log) + + hass.services.async_register( + 'test_domain', 'test_service', protected_mock_service, schema=None) + + with pytest.raises(exceptions.Unauthorized): + await hass.services.async_call( + 'test_domain', + 'test_service', {}, + blocking=True, + context=ha.Context(user_id=hass_read_only_user.id)) + + +async def test_domain_control_admin(hass, hass_admin_user, mock_entities): + """Test domain verification in a service call with an admin user.""" + calls = [] + + async def mock_service_log(call): + """Define a protected service.""" + calls.append(call) + + with patch('homeassistant.helpers.entity_registry.async_get_registry', + return_value=mock_coro(Mock(entities=mock_entities))): + protected_mock_service = hass.helpers.service.verify_domain_control( + 'test_domain')(mock_service_log) + + hass.services.async_register( + 'test_domain', 'test_service', protected_mock_service, schema=None) + + await hass.services.async_call( + 'test_domain', + 'test_service', {}, + blocking=True, + context=ha.Context(user_id=hass_admin_user.id)) + + assert len(calls) == 1 + + +async def test_domain_control_no_user(hass, mock_entities): + """Test domain verification in a service call with no user.""" + calls = [] + + async def mock_service_log(call): + """Define a protected service.""" + calls.append(call) + + with patch('homeassistant.helpers.entity_registry.async_get_registry', + return_value=mock_coro(Mock(entities=mock_entities))): + protected_mock_service = hass.helpers.service.verify_domain_control( + 'test_domain')(mock_service_log) + + hass.services.async_register( + 'test_domain', 'test_service', protected_mock_service, schema=None) + + await hass.services.async_call( + 'test_domain', + 'test_service', {}, + blocking=True, + context=ha.Context(user_id=None)) + + assert len(calls) == 1 From e8343452cd4702a61166fd74d78323bf95092f7c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 13 Apr 2019 13:17:01 -0700 Subject: [PATCH 600/605] Validate component usage (#23037) * Update manifest validator * Update circle * Update text * Typo * fix link to codeowners * Merge CODEOWNERS into hassfest * Annotate errors with fixable * Convert error to warning * Lint * Make abs path * Python 3.5... * Typo * Fix tests --- .circleci/config.yml | 8 +- homeassistant/components/cloud/manifest.json | 3 +- homeassistant/components/demo/manifest.json | 4 +- homeassistant/components/hassio/manifest.json | 3 +- homeassistant/components/map/manifest.json | 4 +- .../components/panel_custom/__init__.py | 5 +- .../components/websocket_api/__init__.py | 2 +- .../components/websocket_api/commands.py | 17 ++- script/gen_requirements_all.py | 27 ++++- script/hassfest/__init__.py | 1 + script/hassfest/__main__.py | 84 +++++++++++++++ script/hassfest/codeowners.py | 85 +++++++++++++++ script/hassfest/dependencies.py | 65 ++++++++++++ script/hassfest/manifest.py | 40 +++++++ .../{manifest => hassfest}/manifest_helper.py | 0 script/hassfest/model.py | 91 ++++++++++++++++ script/manifest/codeowners.py | 74 ------------- script/manifest/requirements.py | 22 ---- script/manifest/validate.py | 100 ------------------ 19 files changed, 415 insertions(+), 220 deletions(-) create mode 100644 script/hassfest/__init__.py create mode 100644 script/hassfest/__main__.py create mode 100755 script/hassfest/codeowners.py create mode 100644 script/hassfest/dependencies.py create mode 100644 script/hassfest/manifest.py rename script/{manifest => hassfest}/manifest_helper.py (100%) create mode 100644 script/hassfest/model.py delete mode 100755 script/manifest/codeowners.py delete mode 100644 script/manifest/requirements.py delete mode 100755 script/manifest/validate.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 70d2f7af3a32fa..19542b05aee744 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,12 +91,6 @@ jobs: . venv/bin/activate flake8 - - run: - name: validate CODEOWNERS - command: | - . venv/bin/activate - python script/manifest/codeowners.py validate - - run: name: run static type check command: | @@ -110,7 +104,7 @@ jobs: name: validate manifests command: | . venv/bin/activate - python script/manifest/validate.py + python -m script.hassfest validate - run: name: run gen_requirements_all diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index b7822fcd90382e..61e9600302f05c 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -6,7 +6,8 @@ "hass-nabucasa==0.11" ], "dependencies": [ - "http" + "http", + "webhook" ], "codeowners": [ "@home-assistant/core" diff --git a/homeassistant/components/demo/manifest.json b/homeassistant/components/demo/manifest.json index ab079a4c2eed1d..4f167ecae25afa 100644 --- a/homeassistant/components/demo/manifest.json +++ b/homeassistant/components/demo/manifest.json @@ -5,7 +5,9 @@ "requirements": [], "dependencies": [ "conversation", - "zone" + "zone", + "group", + "configurator" ], "codeowners": [ "@home-assistant/core" diff --git a/homeassistant/components/hassio/manifest.json b/homeassistant/components/hassio/manifest.json index be345fb5adbc17..23095064d558aa 100644 --- a/homeassistant/components/hassio/manifest.json +++ b/homeassistant/components/hassio/manifest.json @@ -4,7 +4,8 @@ "documentation": "https://www.home-assistant.io/hassio", "requirements": [], "dependencies": [ - "http" + "http", + "panel_custom" ], "codeowners": [ "@home-assistant/hass-io" diff --git a/homeassistant/components/map/manifest.json b/homeassistant/components/map/manifest.json index 993dfc6577eaba..d26d7d9530fdda 100644 --- a/homeassistant/components/map/manifest.json +++ b/homeassistant/components/map/manifest.json @@ -3,6 +3,8 @@ "name": "Map", "documentation": "https://www.home-assistant.io/components/map", "requirements": [], - "dependencies": [], + "dependencies": [ + "frontend" + ], "codeowners": [] } diff --git a/homeassistant/components/panel_custom/__init__.py b/homeassistant/components/panel_custom/__init__.py index 9367f102441808..f6a4fcdb733930 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -124,9 +124,12 @@ async def async_register_panel( async def async_setup(hass, config): """Initialize custom panel.""" + if DOMAIN not in config: + return True + success = False - for panel in config.get(DOMAIN): + for panel in config[DOMAIN]: name = panel[CONF_COMPONENT_NAME] kwargs = { diff --git a/homeassistant/components/websocket_api/__init__.py b/homeassistant/components/websocket_api/__init__.py index 6c4935b9d950a9..6bb4ea9c1c4bdd 100644 --- a/homeassistant/components/websocket_api/__init__.py +++ b/homeassistant/components/websocket_api/__init__.py @@ -43,5 +43,5 @@ def async_register_command(hass, command_or_handler, handler=None, async def async_setup(hass, config): """Initialize the websocket API.""" hass.http.register_view(http.WebsocketAPIView) - commands.async_register_commands(hass) + commands.async_register_commands(hass, async_register_command) return True diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index 32bbd90aad1eb9..d9834758c80215 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -14,16 +14,15 @@ @callback -def async_register_commands(hass): +def async_register_commands(hass, async_reg): """Register commands.""" - async_reg = hass.components.websocket_api.async_register_command - async_reg(handle_subscribe_events) - async_reg(handle_unsubscribe_events) - async_reg(handle_call_service) - async_reg(handle_get_states) - async_reg(handle_get_services) - async_reg(handle_get_config) - async_reg(handle_ping) + async_reg(hass, handle_subscribe_events) + async_reg(hass, handle_unsubscribe_events) + async_reg(hass, handle_call_service) + async_reg(hass, handle_get_states) + async_reg(hass, handle_get_services) + async_reg(hass, handle_get_config) + async_reg(hass, handle_ping) def pong_message(iden): diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index c8622837cf5dd9..f71b8944d7cb24 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -3,11 +3,12 @@ import fnmatch import importlib import os +import pathlib import pkgutil import re import sys -from script.manifest.requirements import gather_requirements_from_manifests +from script.hassfest.model import Integration COMMENT_REQUIREMENTS = ( 'Adafruit-DHT', @@ -219,7 +220,7 @@ def gather_modules(): errors = [] - gather_requirements_from_manifests(process_requirements, errors, reqs) + gather_requirements_from_manifests(errors, reqs) gather_requirements_from_modules(errors, reqs) for key in reqs: @@ -235,6 +236,28 @@ def gather_modules(): return reqs +def gather_requirements_from_manifests(errors, reqs): + """Gather all of the requirements from manifests.""" + integrations = Integration.load_dir(pathlib.Path( + 'homeassistant/components' + )) + for domain in sorted(integrations): + integration = integrations[domain] + + if not integration.manifest: + errors.append( + 'The manifest for component {} is invalid.'.format(domain) + ) + continue + + process_requirements( + errors, + integration.manifest['requirements'], + 'homeassistant.components.{}'.format(domain), + reqs + ) + + def gather_requirements_from_modules(errors, reqs): """Collect the requirements from the modules directly.""" for package in sorted( diff --git a/script/hassfest/__init__.py b/script/hassfest/__init__.py new file mode 100644 index 00000000000000..2fa7997162f2fd --- /dev/null +++ b/script/hassfest/__init__.py @@ -0,0 +1 @@ +"""Manifest validator.""" diff --git a/script/hassfest/__main__.py b/script/hassfest/__main__.py new file mode 100644 index 00000000000000..2514db6314d81b --- /dev/null +++ b/script/hassfest/__main__.py @@ -0,0 +1,84 @@ +"""Validate manifests.""" +import pathlib +import sys + +from .model import Integration, Config +from . import dependencies, manifest, codeowners + +PLUGINS = [ + manifest, + dependencies, + codeowners, +] + + +def get_config() -> Config: + """Return config.""" + if not pathlib.Path('requirements_all.txt').is_file(): + raise RuntimeError("Run from project root") + + return Config( + root=pathlib.Path('.').absolute(), + action='validate' if sys.argv[-1] == 'validate' else 'generate', + ) + + +def main(): + """Validate manifests.""" + try: + config = get_config() + except RuntimeError as err: + print(err) + return 1 + + integrations = Integration.load_dir( + pathlib.Path('homeassistant/components') + ) + manifest.validate(integrations, config) + dependencies.validate(integrations, config) + codeowners.validate(integrations, config) + + # When we generate, all errors that are fixable will be ignored, + # as generating them will be fixed. + if config.action == 'generate': + general_errors = [err for err in config.errors if not err.fixable] + invalid_itg = [ + itg for itg in integrations.values() + if any( + not error.fixable for error in itg.errors + ) + ] + else: + # action == validate + general_errors = config.errors + invalid_itg = [itg for itg in integrations.values() if itg.errors] + + print("Integrations:", len(integrations)) + print("Invalid integrations:", len(invalid_itg)) + + if not invalid_itg and not general_errors: + codeowners.generate(integrations, config) + return 0 + + print() + if config.action == 'generate': + print("Found errors. Generating files canceled.") + print() + + if general_errors: + print("General errors:") + for error in general_errors: + print("*", error) + print() + + for integration in sorted(invalid_itg, key=lambda itg: itg.domain): + print("Integration {}:".format(integration.domain)) + for error in integration.errors: + print("*", error) + print() + + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/hassfest/codeowners.py b/script/hassfest/codeowners.py new file mode 100755 index 00000000000000..8ba2008f1cdbb0 --- /dev/null +++ b/script/hassfest/codeowners.py @@ -0,0 +1,85 @@ +"""Generate CODEOWNERS.""" +from typing import Dict + +from .model import Integration, Config + +BASE = """ +# This file is generated by script/manifest/codeowners.py +# People marked here will be automatically requested for a review +# when the code that they own is touched. +# https://github.com/blog/2392-introducing-code-owners + +# Home Assistant Core +setup.py @home-assistant/core +homeassistant/*.py @home-assistant/core +homeassistant/helpers/* @home-assistant/core +homeassistant/util/* @home-assistant/core + +# Virtualization +Dockerfile @home-assistant/docker +virtualization/Docker/* @home-assistant/docker + +# Other code +homeassistant/scripts/check_config.py @kellerza + +# Integrations +""".strip() + +INDIVIDUAL_FILES = """ +# Individual files +homeassistant/components/group/cover @cdce8p +homeassistant/components/demo/weather @fabaff +""" + + +def generate_and_validate(integrations: Dict[str, Integration]): + """Generate CODEOWNERS.""" + parts = [BASE] + + for domain in sorted(integrations): + integration = integrations[domain] + + if not integration.manifest: + continue + + codeowners = integration.manifest['codeowners'] + + if not codeowners: + continue + + for owner in codeowners: + if not owner.startswith('@'): + integration.add_error( + 'codeowners', + 'Code owners need to be valid GitHub handles.', + ) + + parts.append("homeassistant/components/{}/* {}".format( + domain, ' '.join(codeowners))) + + parts.append('\n' + INDIVIDUAL_FILES.strip()) + + return '\n'.join(parts) + + +def validate(integrations: Dict[str, Integration], config: Config): + """Validate CODEOWNERS.""" + codeowners_path = config.root / 'CODEOWNERS' + config.cache['codeowners'] = content = generate_and_validate(integrations) + + with open(str(codeowners_path), 'r') as fp: + if fp.read().strip() != content: + config.add_error( + "codeowners", + "File CODEOWNERS is not up to date. " + "Run python3 -m script.hassfest", + fixable=True + ) + return + + +def generate(integrations: Dict[str, Integration], config: Config): + """Generate CODEOWNERS.""" + codeowners_path = config.root / 'CODEOWNERS' + with open(str(codeowners_path), 'w') as fp: + fp.write(config.cache['codeowners'] + '\n') diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py new file mode 100644 index 00000000000000..af8a782906bbb8 --- /dev/null +++ b/script/hassfest/dependencies.py @@ -0,0 +1,65 @@ +"""Validate dependencies.""" +import pathlib +import re +from typing import Set, Dict + +from .model import Integration + + +def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) \ + -> Set[str]: + """Recursively go through a dir and it's children and find the regex.""" + pattern = re.compile(search_pattern) + found = set() + + for fil in path.glob(glob_pattern): + if not fil.is_file(): + continue + + for match in pattern.finditer(fil.read_text()): + found.add(match.groups()[0]) + + return found + + +# These components will always be set up +ALLOWED_USED_COMPONENTS = { + 'persistent_notification', +} + + +def validate_dependencies(integration: Integration): + """Validate all dependencies.""" + # Find usage of hass.components + referenced = grep_dir(integration.path, "**/*.py", + r"hass\.components\.(\w+)") + referenced -= ALLOWED_USED_COMPONENTS + referenced -= set(integration.manifest['dependencies']) + + if referenced: + for domain in sorted(referenced): + print("Warning: {} references integration {} but it's not a " + "dependency".format(integration.domain, domain)) + # Not enforced yet. + # integration.add_error( + # 'dependencies', + # "Using component {} but it's not a dependency".format(domain) + # ) + + +def validate(integrations: Dict[str, Integration], config): + """Handle dependencies for integrations.""" + # check for non-existing dependencies + for integration in integrations.values(): + if not integration.manifest: + continue + + validate_dependencies(integration) + + # check that all referenced dependencies exist + for dep in integration.manifest['dependencies']: + if dep not in integrations: + integration.add_error( + 'dependencies', + "Dependency {} does not exist" + ) diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py new file mode 100644 index 00000000000000..b644ec7d055f1d --- /dev/null +++ b/script/hassfest/manifest.py @@ -0,0 +1,40 @@ +"""Manifest validation.""" +from typing import Dict + +import voluptuous as vol +from voluptuous.humanize import humanize_error + +from .model import Integration + + +MANIFEST_SCHEMA = vol.Schema({ + vol.Required('domain'): str, + vol.Required('name'): str, + vol.Required('documentation'): str, + vol.Required('requirements'): [str], + vol.Required('dependencies'): [str], + vol.Required('codeowners'): [str], +}) + + +def validate_manifest(integration: Integration): + """Validate manifest.""" + try: + MANIFEST_SCHEMA(integration.manifest) + except vol.Invalid as err: + integration.add_error( + 'manifest', + "Invalid manifest: {}".format( + humanize_error(integration.manifest, err))) + integration.manifest = None + return + + if integration.manifest['domain'] != integration.path.name: + integration.add_error('manifest', 'Domain does not match dir name') + + +def validate(integrations: Dict[str, Integration], config): + """Handle all integrations manifests.""" + for integration in integrations.values(): + if integration.manifest: + validate_manifest(integration) diff --git a/script/manifest/manifest_helper.py b/script/hassfest/manifest_helper.py similarity index 100% rename from script/manifest/manifest_helper.py rename to script/hassfest/manifest_helper.py diff --git a/script/hassfest/model.py b/script/hassfest/model.py new file mode 100644 index 00000000000000..c2a72ebd5090ed --- /dev/null +++ b/script/hassfest/model.py @@ -0,0 +1,91 @@ +"""Models for manifest validator.""" +import json +from typing import List, Dict, Any +import pathlib + +import attr + + +@attr.s +class Error: + """Error validating an integration.""" + + plugin = attr.ib(type=str) + error = attr.ib(type=str) + fixable = attr.ib(type=bool, default=False) + + def __str__(self) -> str: + """Represent error as string.""" + return "[{}] {}".format(self.plugin.upper(), self.error) + + +@attr.s +class Config: + """Config for the run.""" + + root = attr.ib(type=pathlib.Path) + action = attr.ib(type=str) + errors = attr.ib(type=List[Error], factory=list) + cache = attr.ib(type=Dict[str, Any], factory=dict) + + def add_error(self, *args, **kwargs): + """Add an error.""" + self.errors.append(Error(*args, **kwargs)) + + +@attr.s +class Integration: + """Represent an integration in our validator.""" + + @classmethod + def load_dir(cls, path: pathlib.Path): + """Load all integrations in a directory.""" + assert path.is_dir() + integrations = {} + for fil in path.iterdir(): + if fil.is_file() or fil.name == '__pycache__': + continue + + integration = cls(fil) + integration.load_manifest() + integrations[integration.domain] = integration + + return integrations + + path = attr.ib(type=pathlib.Path) + manifest = attr.ib(type=dict, default=None) + errors = attr.ib(type=List[Error], factory=list) + + @property + def domain(self) -> str: + """Integration domain.""" + return self.path.name + + @property + def manifest_path(self) -> pathlib.Path: + """Integration manifest path.""" + return self.path / 'manifest.json' + + def add_error(self, *args, **kwargs): + """Add an error.""" + self.errors.append(Error(*args, **kwargs)) + + def load_manifest(self) -> None: + """Load manifest.""" + if not self.manifest_path.is_file(): + self.add_error( + 'model', + "Manifest file {} not found".format(self.manifest_path) + ) + return + + try: + manifest = json.loads(self.manifest_path.read_text()) + except ValueError as err: + self.add_error( + 'model', + "Manifest contains invalid JSON: {}".format(err) + ) + return + + self.manifest = manifest diff --git a/script/manifest/codeowners.py b/script/manifest/codeowners.py deleted file mode 100755 index 96b2b252e3d4e1..00000000000000 --- a/script/manifest/codeowners.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -"""Generate CODEOWNERS.""" -import os -import sys - -from manifest_helper import iter_manifests - -BASE = """ -# This file is generated by script/manifest/codeowners.py -# People marked here will be automatically requested for a review -# when the code that they own is touched. -# https://github.com/blog/2392-introducing-code-owners - -# Home Assistant Core -setup.py @home-assistant/core -homeassistant/*.py @home-assistant/core -homeassistant/helpers/* @home-assistant/core -homeassistant/util/* @home-assistant/core - -# Virtualization -Dockerfile @home-assistant/docker -virtualization/Docker/* @home-assistant/docker - -# Other code -homeassistant/scripts/check_config.py @kellerza - -# Integrations -""" - -INDIVIDUAL_FILES = """ -# Individual files -homeassistant/components/group/cover @cdce8p -homeassistant/components/demo/weather @fabaff -""" - - -def generate(): - """Generate CODEOWNERS.""" - parts = [BASE.strip()] - - for manifest in iter_manifests(): - if not manifest['codeowners']: - continue - - parts.append("homeassistant/components/{}/* {}".format( - manifest['domain'], ' '.join(manifest['codeowners']))) - - parts.append('\n' + INDIVIDUAL_FILES.strip()) - - return '\n'.join(parts) - - -def main(validate): - """Runner for CODEOWNERS gen.""" - if not os.path.isfile('requirements_all.txt'): - print('Run this from HA root dir') - return 1 - - content = generate() - - if validate: - with open('CODEOWNERS', 'r') as fp: - if fp.read().strip() != content: - print("CODEOWNERS is not up to date. " - "Run python script/manifest/codeowners.py") - return 1 - return 0 - - with open('CODEOWNERS', 'w') as fp: - fp.write(content + '\n') - - -if __name__ == '__main__': - sys.exit(main(sys.argv[-1] == 'validate')) diff --git a/script/manifest/requirements.py b/script/manifest/requirements.py deleted file mode 100644 index 5a370510484aba..00000000000000 --- a/script/manifest/requirements.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Helpers to gather requirements from manifests.""" -from .manifest_helper import iter_manifests - - -def gather_requirements_from_manifests(process_requirements, errors, reqs): - """Gather all of the requirements from manifests.""" - for manifest in iter_manifests(): - assert manifest['domain'] - - if manifest.get('requirements') is None: - errors.append( - 'The manifest for component {} is invalid. Please run' - 'script/manifest/validate.py'.format(manifest['domain']) - ) - continue - - process_requirements( - errors, - manifest['requirements'], - 'homeassistant.components.{}'.format(manifest['domain']), - reqs - ) diff --git a/script/manifest/validate.py b/script/manifest/validate.py deleted file mode 100755 index e5db1c9368c530..00000000000000 --- a/script/manifest/validate.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -"""Validate all integrations have manifests and that they are valid.""" -import json -import pathlib -import sys - -import voluptuous as vol -from voluptuous.humanize import humanize_error - - -MANIFEST_SCHEMA = vol.Schema({ - vol.Required('domain'): str, - vol.Required('name'): str, - vol.Required('documentation'): str, - vol.Required('requirements'): [str], - vol.Required('dependencies'): [str], - vol.Required('codeowners'): [str], -}) - - -COMPONENTS_PATH = pathlib.Path('homeassistant/components') - - -def validate_dependency(path, dependency, loaded, loading): - """Validate dependency is exist and no circular dependency.""" - dep_path = path.parent / dependency - return validate_integration(dep_path, loaded, loading) - - -def validate_integration(path, loaded, loading): - """Validate that an integrations has a valid manifest.""" - errors = [] - path = pathlib.Path(path) - - manifest_path = path / 'manifest.json' - - if not manifest_path.is_file(): - errors.append('Manifest file {} not found'.format(manifest_path)) - return errors # Fatal error - - try: - manifest = json.loads(manifest_path.read_text()) - except ValueError as err: - errors.append("Manifest contains invalid JSON: {}".format(err)) - return errors # Fatal error - - try: - MANIFEST_SCHEMA(manifest) - except vol.Invalid as err: - errors.append(humanize_error(manifest, err)) - - if manifest['domain'] != path.name: - errors.append('Domain does not match dir name') - - for dep in manifest['dependencies']: - if dep in loaded: - continue - if dep in loading: - errors.append("Found circular dependency {} in {}".format( - dep, path - )) - continue - loading.add(dep) - - errors.extend(validate_dependency(path, dep, loaded, loading)) - - loaded.add(path.name) - return errors - - -def validate_all(): - """Validate all integrations.""" - invalid = [] - - for fil in COMPONENTS_PATH.iterdir(): - if fil.is_file() or fil.name == '__pycache__': - continue - - errors = validate_integration(fil, set(), set()) - - if errors: - invalid.append((fil, errors)) - - if not invalid: - return 0 - - print("Found invalid manifests") - print() - - for integration, errors in invalid: - print(integration) - for error in errors: - print("*", error) - print() - - return 1 - - -if __name__ == '__main__': - sys.exit(validate_all()) From 46ee7d7b22074dc2f3a7dccf432beb2ac0f24fbb Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 13 Apr 2019 15:32:07 -0600 Subject: [PATCH 601/605] Fix test (#23081) --- homeassistant/helpers/service.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 58f127ac707e3a..1cfbf9e3c5fdb3 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -19,8 +19,6 @@ from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.helpers.typing import HomeAssistantType -from .typing import HomeAssistantType - CONF_SERVICE = 'service' CONF_SERVICE_TEMPLATE = 'service_template' CONF_SERVICE_ENTITY_ID = 'entity_id' From 8c89e260dfd5ee9bd4283d32d952600a06bbc365 Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Sat, 13 Apr 2019 16:44:45 -0500 Subject: [PATCH 602/605] HEOS confirm discovered devices before adding (#23063) * Add host selection step to discovery * Review feedback * Fix failing test --- homeassistant/components/heos/config_flow.py | 37 ++++++---- homeassistant/components/heos/const.py | 1 + homeassistant/components/heos/strings.json | 4 +- tests/components/heos/test_config_flow.py | 72 +++++++++++--------- 4 files changed, 66 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/heos/config_flow.py b/homeassistant/components/heos/config_flow.py index 66650531cadfaa..656058877db9cd 100644 --- a/homeassistant/components/heos/config_flow.py +++ b/homeassistant/components/heos/config_flow.py @@ -4,9 +4,9 @@ import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_NAME -from .const import DOMAIN +from .const import DATA_DISCOVERED_HOSTS, DOMAIN def format_title(host: str) -> str: @@ -23,13 +23,17 @@ class HeosFlowHandler(config_entries.ConfigFlow): async def async_step_discovery(self, discovery_info): """Handle a discovered Heos device.""" - # Only continue if this is the only active flow - flows = self.hass.config_entries.flow.async_progress() - heos_flows = [flow for flow in flows if flow['handler'] == DOMAIN] - if len(heos_flows) == 1: - return await self.async_step_user( - {CONF_HOST: discovery_info[CONF_HOST]}) - return self.async_abort(reason='already_setup') + # Store discovered host + friendly_name = "{} ({})".format( + discovery_info[CONF_NAME], discovery_info[CONF_HOST]) + self.hass.data.setdefault(DATA_DISCOVERED_HOSTS, {}) + self.hass.data[DATA_DISCOVERED_HOSTS][friendly_name] \ + = discovery_info[CONF_HOST] + # Abort if other flows in progress or an entry already exists + if self._async_in_progress() or self._async_current_entries(): + return self.async_abort(reason='already_setup') + # Show selection form + return self.async_show_form(step_id='user') async def async_step_import(self, user_input=None): """Occurs when an entry is setup through config.""" @@ -41,30 +45,33 @@ async def async_step_import(self, user_input=None): async def async_step_user(self, user_input=None): """Obtain host and validate connection.""" from pyheos import Heos - + self.hass.data.setdefault(DATA_DISCOVERED_HOSTS, {}) # Only a single entry is needed for all devices - entries = self.hass.config_entries.async_entries(DOMAIN) - if entries: + if self._async_current_entries(): return self.async_abort(reason='already_setup') - # Try connecting to host if provided errors = {} host = None if user_input is not None: host = user_input[CONF_HOST] + # Map host from friendly name if in discovered hosts + host = self.hass.data[DATA_DISCOVERED_HOSTS].get(host, host) heos = Heos(host) try: await heos.connect() - return await self.async_step_import(user_input) + self.hass.data.pop(DATA_DISCOVERED_HOSTS) + return await self.async_step_import({CONF_HOST: host}) except (asyncio.TimeoutError, ConnectionError): errors[CONF_HOST] = 'connection_failure' finally: await heos.disconnect() # Return form + host_type = str if not self.hass.data[DATA_DISCOVERED_HOSTS] \ + else vol.In(list(self.hass.data[DATA_DISCOVERED_HOSTS])) return self.async_show_form( step_id='user', data_schema=vol.Schema({ - vol.Required(CONF_HOST, default=host): str + vol.Required(CONF_HOST, default=host): host_type }), errors=errors) diff --git a/homeassistant/components/heos/const.py b/homeassistant/components/heos/const.py index 9cb65589b438e4..fc3a7fd8f30163 100644 --- a/homeassistant/components/heos/const.py +++ b/homeassistant/components/heos/const.py @@ -4,5 +4,6 @@ COMMAND_RETRY_DELAY = 1 DATA_CONTROLLER = "controller" DATA_SOURCE_MANAGER = "source_manager" +DATA_DISCOVERED_HOSTS = "heos_discovered_hosts" DOMAIN = 'heos' SIGNAL_HEOS_SOURCES_UPDATED = "heos_sources_updated" diff --git a/homeassistant/components/heos/strings.json b/homeassistant/components/heos/strings.json index a272c0a2a0fd88..b210e0ba87f2cc 100644 --- a/homeassistant/components/heos/strings.json +++ b/homeassistant/components/heos/strings.json @@ -1,12 +1,12 @@ { "config": { - "title": "Heos", + "title": "HEOS", "step": { "user": { "title": "Connect to Heos", "description": "Please enter the host name or IP address of a Heos device (preferably one connected via wire to the network).", "data": { - "access_token": "Host" + "host": "Host" } } }, diff --git a/tests/components/heos/test_config_flow.py b/tests/components/heos/test_config_flow.py index 9c33cbee7aaae9..ade0100dbd6c5c 100644 --- a/tests/components/heos/test_config_flow.py +++ b/tests/components/heos/test_config_flow.py @@ -3,8 +3,8 @@ from homeassistant import data_entry_flow from homeassistant.components.heos.config_flow import HeosFlowHandler -from homeassistant.components.heos.const import DOMAIN -from homeassistant.const import CONF_HOST +from homeassistant.components.heos.const import DATA_DISCOVERED_HOSTS, DOMAIN +from homeassistant.const import CONF_HOST, CONF_NAME async def test_flow_aborts_already_setup(hass, config_entry): @@ -58,41 +58,51 @@ async def test_create_entry_when_host_valid(hass, controller): assert controller.disconnect.call_count == 1 -async def test_create_entry_with_discovery(hass, controller, discovery_data): - """Test discovery creates entry.""" +async def test_create_entry_when_friendly_name_valid(hass, controller): + """Test result type is create entry when friendly name is valid.""" + hass.data[DATA_DISCOVERED_HOSTS] = {"Office (127.0.0.1)": "127.0.0.1"} + flow = HeosFlowHandler() + flow.hass = hass + data = {CONF_HOST: "Office (127.0.0.1)"} + result = await flow.async_step_user(data) + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == 'Controller (127.0.0.1)' + assert result['data'] == {CONF_HOST: "127.0.0.1"} + assert controller.connect.call_count == 1 + assert controller.disconnect.call_count == 1 + assert DATA_DISCOVERED_HOSTS not in hass.data + + +async def test_discovery_shows_create_form(hass, controller, discovery_data): + """Test discovery shows form to confirm setup and subsequent abort.""" await hass.config_entries.flow.async_init( DOMAIN, context={'source': 'discovery'}, data=discovery_data) await hass.async_block_till_done() - entries = hass.config_entries.async_entries(DOMAIN) - assert len(entries) == 1 - assert entries[0].data == {CONF_HOST: discovery_data[CONF_HOST]} - assert entries[0].title == 'Controller (127.0.0.1)' + assert len(hass.config_entries.flow.async_progress()) == 1 + assert hass.data[DATA_DISCOVERED_HOSTS] == { + "Office (127.0.0.1)": "127.0.0.1" + } - -async def test_entry_already_exists_discovery( - hass, controller, discovery_data, config_entry): - """Test discovery does not create multiple entries when already setup.""" - config_entry.add_to_hass(hass) + discovery_data[CONF_HOST] = "127.0.0.2" + discovery_data[CONF_NAME] = "Bedroom" await hass.config_entries.flow.async_init( DOMAIN, context={'source': 'discovery'}, data=discovery_data) await hass.async_block_till_done() - entries = hass.config_entries.async_entries(DOMAIN) - assert len(entries) == 1 - - -async def test_multiple_discovery_creates_single_entry( - hass, controller, discovery_data): - """Test discovery of multiple devices creates a single entry.""" - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={'source': 'discovery'}, - data={CONF_HOST: discovery_data})) - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={'source': 'discovery'}, - data={CONF_HOST: discovery_data})) - await hass.async_block_till_done() - entries = hass.config_entries.async_entries(DOMAIN) - assert len(entries) == 1 + assert len(hass.config_entries.flow.async_progress()) == 1 + assert hass.data[DATA_DISCOVERED_HOSTS] == { + "Office (127.0.0.1)": "127.0.0.1", + "Bedroom (127.0.0.2)": "127.0.0.2" + } + + +async def test_disovery_flow_aborts_already_setup( + hass, controller, discovery_data, config_entry): + """Test discovery flow aborts when entry already setup.""" + config_entry.add_to_hass(hass) + flow = HeosFlowHandler() + flow.hass = hass + result = await flow.async_step_discovery(discovery_data) + assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert result['reason'] == 'already_setup' From 39264af310255b0bd011a07fa754d0874a0adcf6 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Sat, 13 Apr 2019 17:50:21 -0400 Subject: [PATCH 603/605] Add missing async for tplink's async_setup_platform methods (#23066) * add missing async for tplink's async_setup_platform methods thanks to @MartinHjelmare for spotting this, related to #21916 * fix line lengths --- homeassistant/components/tplink/light.py | 3 ++- homeassistant/components/tplink/switch.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index 6fa795bcafcad5..dc2fcce949a5ad 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -21,7 +21,8 @@ ATTR_MONTHLY_ENERGY_KWH = 'monthly_energy_kwh' -def async_setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform(hass, config, add_entities, + discovery_info=None): """Set up the platform. Deprecated. diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index 3040b52cd2245b..a3d680a0a50186 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -17,7 +17,8 @@ ATTR_CURRENT_A = 'current_a' -def async_setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform(hass, config, add_entities, + discovery_info=None): """Set up the platform. Deprecated. From 0a0975b5d94b975605586fef22888a091daa0f95 Mon Sep 17 00:00:00 2001 From: Martin Fuchs <39280548+fucm@users.noreply.github.com> Date: Sat, 13 Apr 2019 23:53:36 +0200 Subject: [PATCH 604/605] Add support for Stiebel Eltron heat pumps (#21199) * Start with Stiebel Eltron heatpump * STE HP * Add read of operating mode * Add read-write operation mode * Further extract ModBus access * Separation of platform and API * Last changes * Use modbus hub * Update module doc with config * Clean up platform code * Cleanup and update to dev2 of pystiebeleltron * Remove slave configuration * Add translation of states * Make name parameter optional * Consolidate platform * Correct .coveragerc after conflict * Prepare component for sensor platform * Fix issues found in review * Remove custom states and map to existing HA states * Force update, when values are modified * Update CODEOWNERS and requirements_all.txt * Fix .coveragerc file * Exclude stiebel_eltron components in .coveragerc * Break out to module level constant * Rename constant * Removed REQ and DEP constant. --- .coveragerc | 1 + CODEOWNERS | 1 + .../components/stiebel_eltron/__init__.py | 59 +++++++ .../components/stiebel_eltron/climate.py | 149 ++++++++++++++++++ .../components/stiebel_eltron/manifest.json | 14 ++ requirements_all.txt | 3 + 6 files changed, 227 insertions(+) create mode 100644 homeassistant/components/stiebel_eltron/__init__.py create mode 100644 homeassistant/components/stiebel_eltron/climate.py create mode 100644 homeassistant/components/stiebel_eltron/manifest.json diff --git a/.coveragerc b/.coveragerc index ba6f27d3655a5a..53fe5f306b2697 100644 --- a/.coveragerc +++ b/.coveragerc @@ -559,6 +559,7 @@ omit = homeassistant/components/srp_energy/sensor.py homeassistant/components/starlingbank/sensor.py homeassistant/components/steam_online/sensor.py + homeassistant/components/stiebel_eltron/* homeassistant/components/stride/notify.py homeassistant/components/supervisord/sensor.py homeassistant/components/swiss_hydrological_data/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 2b45acea2e1b47..8775e8867871ff 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -195,6 +195,7 @@ homeassistant/components/spaceapi/* @fabaff homeassistant/components/spider/* @peternijssen homeassistant/components/sql/* @dgomes homeassistant/components/statistics/* @fabaff +homeassistant/components/stiebel_eltron/* @fucm homeassistant/components/sun/* @home-assistant/core homeassistant/components/swiss_hydrological_data/* @fabaff homeassistant/components/swiss_public_transport/* @fabaff diff --git a/homeassistant/components/stiebel_eltron/__init__.py b/homeassistant/components/stiebel_eltron/__init__.py new file mode 100644 index 00000000000000..52dc2d848918bf --- /dev/null +++ b/homeassistant/components/stiebel_eltron/__init__.py @@ -0,0 +1,59 @@ +"""The component for STIEBEL ELTRON heat pumps with ISGWeb Modbus module.""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.modbus import ( + CONF_HUB, DEFAULT_HUB, DOMAIN as MODBUS_DOMAIN) +from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME +from homeassistant.helpers import discovery +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle + +DOMAIN = 'stiebel_eltron' + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_NAME, default=DEVICE_DEFAULT_NAME): cv.string, + vol.Optional(CONF_HUB, default=DEFAULT_HUB): cv.string, + }) +}, extra=vol.ALLOW_EXTRA) + +_LOGGER = logging.getLogger(__name__) + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + + +def setup(hass, config): + """Set up the STIEBEL ELTRON unit. + + Will automatically load climate platform. + """ + name = config[DOMAIN][CONF_NAME] + modbus_client = hass.data[MODBUS_DOMAIN][config[DOMAIN][CONF_HUB]] + + hass.data[DOMAIN] = { + 'name': name, + 'ste_data': StiebelEltronData(name, modbus_client) + } + + discovery.load_platform(hass, 'climate', DOMAIN, {}, config) + return True + + +class StiebelEltronData: + """Get the latest data and update the states.""" + + def __init__(self, name, modbus_client): + """Init the STIEBEL ELTRON data object.""" + from pystiebeleltron import pystiebeleltron + self.api = pystiebeleltron.StiebelEltronAPI(modbus_client, 1) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Update unit data.""" + if not self.api.update(): + _LOGGER.warning("Modbus read failed") + else: + _LOGGER.debug("Data updated successfully") diff --git a/homeassistant/components/stiebel_eltron/climate.py b/homeassistant/components/stiebel_eltron/climate.py new file mode 100644 index 00000000000000..fc6038d95ad6ca --- /dev/null +++ b/homeassistant/components/stiebel_eltron/climate.py @@ -0,0 +1,149 @@ +"""Support for stiebel_eltron climate platform.""" +import logging + +from homeassistant.components.climate import ClimateDevice +from homeassistant.components.climate.const import ( + STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ( + ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS) + +from . import DOMAIN as STE_DOMAIN + +DEPENDENCIES = ['stiebel_eltron'] + +_LOGGER = logging.getLogger(__name__) + + +SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE +OPERATION_MODES = [STATE_AUTO, STATE_MANUAL, STATE_ECO, STATE_OFF] + +# Mapping STIEBEL ELTRON states to homeassistant states. +STE_TO_HA_STATE = {'AUTOMATIC': STATE_AUTO, + 'MANUAL MODE': STATE_MANUAL, + 'STANDBY': STATE_ECO, + 'DAY MODE': STATE_ON, + 'SETBACK MODE': STATE_ON, + 'DHW': STATE_OFF, + 'EMERGENCY OPERATION': STATE_ON} + +# Mapping homeassistant states to STIEBEL ELTRON states. +HA_TO_STE_STATE = {value: key for key, value in STE_TO_HA_STATE.items()} + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the StiebelEltron platform.""" + name = hass.data[STE_DOMAIN]['name'] + ste_data = hass.data[STE_DOMAIN]['ste_data'] + + add_entities([StiebelEltron(name, ste_data)], True) + + +class StiebelEltron(ClimateDevice): + """Representation of a STIEBEL ELTRON heat pump.""" + + def __init__(self, name, ste_data): + """Initialize the unit.""" + self._name = name + self._target_temperature = None + self._current_temperature = None + self._current_humidity = None + self._operation_modes = OPERATION_MODES + self._current_operation = None + self._filter_alarm = None + self._force_update = False + self._ste_data = ste_data + + @property + def supported_features(self): + """Return the list of supported features.""" + return SUPPORT_FLAGS + + def update(self): + """Update unit attributes.""" + self._ste_data.update(no_throttle=self._force_update) + self._force_update = False + + self._target_temperature = self._ste_data.api.get_target_temp() + self._current_temperature = self._ste_data.api.get_current_temp() + self._current_humidity = self._ste_data.api.get_current_humidity() + self._filter_alarm = self._ste_data.api.get_filter_alarm_status() + self._current_operation = self._ste_data.api.get_operation() + + _LOGGER.debug("Update %s, current temp: %s", self._name, + self._current_temperature) + + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + return { + 'filter_alarm': self._filter_alarm + } + + @property + def name(self): + """Return the name of the climate device.""" + return self._name + + # Handle SUPPORT_TARGET_TEMPERATURE + @property + def temperature_unit(self): + """Return the unit of measurement.""" + return TEMP_CELSIUS + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._current_temperature + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + return self._target_temperature + + @property + def target_temperature_step(self): + """Return the supported step of target temperature.""" + return 0.1 + + @property + def min_temp(self): + """Return the minimum temperature.""" + return 10.0 + + @property + def max_temp(self): + """Return the maximum temperature.""" + return 30.0 + + def set_temperature(self, **kwargs): + """Set new target temperature.""" + target_temperature = kwargs.get(ATTR_TEMPERATURE) + if target_temperature is not None: + _LOGGER.debug("set_temperature: %s", target_temperature) + self._ste_data.api.set_target_temp(target_temperature) + self._force_update = True + + @property + def current_humidity(self): + """Return the current humidity.""" + return float("{0:.1f}".format(self._current_humidity)) + + # Handle SUPPORT_OPERATION_MODE + @property + def operation_list(self): + """List of the operation modes.""" + return self._operation_modes + + @property + def current_operation(self): + """Return current operation ie. heat, cool, idle.""" + return STE_TO_HA_STATE.get(self._current_operation) + + def set_operation_mode(self, operation_mode): + """Set new operation mode.""" + new_mode = HA_TO_STE_STATE.get(operation_mode) + _LOGGER.debug("set_operation_mode: %s -> %s", self._current_operation, + new_mode) + self._ste_data.api.set_operation(new_mode) + self._force_update = True diff --git a/homeassistant/components/stiebel_eltron/manifest.json b/homeassistant/components/stiebel_eltron/manifest.json new file mode 100644 index 00000000000000..0f8b586a9c2d6a --- /dev/null +++ b/homeassistant/components/stiebel_eltron/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "stiebel_eltron", + "name": "STIEBEL ELTRON", + "documentation": "https://www.home-assistant.io/components/stiebel_eltron", + "requirements": [ + "pystiebeleltron==0.0.1.dev2" + ], + "dependencies": [ + "modbus" + ], + "codeowners": [ + "@fucm" + ] +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index c37cceb1a123a3..6ab9c8d1d1e853 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1281,6 +1281,9 @@ pysonos==0.0.10 # homeassistant.components.spc pyspcwebgw==0.4.0 +# homeassistant.components.stiebel_eltron +pystiebeleltron==0.0.1.dev2 + # homeassistant.components.stride pystride==0.1.7 From 56b08a6ddb491c2fb673129693f8be2ea4eb60a9 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sat, 13 Apr 2019 16:50:19 -0600 Subject: [PATCH 605/605] Ensure OpenUV service checks permissions (#22668) * Create decorator to check service permissions * Ensure OpenUV service has proper user permissions * Reverting strange changes --- homeassistant/components/openuv/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/openuv/__init__.py b/homeassistant/components/openuv/__init__.py index 8e8401bbeac36c..63d2744cd4df0d 100644 --- a/homeassistant/components/openuv/__init__.py +++ b/homeassistant/components/openuv/__init__.py @@ -11,6 +11,7 @@ from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity +from homeassistant.helpers.service import verify_domain_control from .config_flow import configured_instances from .const import DOMAIN @@ -130,6 +131,8 @@ async def async_setup_entry(hass, config_entry): from pyopenuv import Client from pyopenuv.errors import OpenUvError + _verify_domain_control = verify_domain_control(hass, DOMAIN) + try: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( @@ -155,6 +158,7 @@ async def async_setup_entry(hass, config_entry): hass.config_entries.async_forward_entry_setup( config_entry, component)) + @_verify_domain_control async def update_data(service): """Refresh OpenUV data.""" _LOGGER.debug('Refreshing OpenUV data')